aboutsummaryrefslogtreecommitdiff
path: root/3rdparty/imgui
diff options
context:
space:
mode:
authorrtk0c <[email protected]>2022-05-08 11:18:13 -0700
committerrtk0c <[email protected]>2022-05-08 11:18:13 -0700
commitec1b60f8d87b30bbd3c56345d9cc693205aa0d4d (patch)
tree816d852690816178a56a8651a6a1d39d73e21212 /3rdparty/imgui
parentcb4f56fc221d89b6f237ad0e9fdab7fc23e3e877 (diff)
Changeset: 32 Branch comment: [] Update imgui to the docking branchimgui-docking
Diffstat (limited to '3rdparty/imgui')
-rw-r--r--3rdparty/imgui/README.md4
-rw-r--r--3rdparty/imgui/source/backends/imgui_impl_glfw.cpp576
-rw-r--r--3rdparty/imgui/source/backends/imgui_impl_glfw.h7
-rw-r--r--3rdparty/imgui/source/backends/imgui_impl_opengl2.cpp42
-rw-r--r--3rdparty/imgui/source/backends/imgui_impl_opengl2.h1
-rw-r--r--3rdparty/imgui/source/backends/imgui_impl_opengl3.cpp42
-rw-r--r--3rdparty/imgui/source/backends/imgui_impl_opengl3.h3
-rw-r--r--3rdparty/imgui/source/imconfig.h15
-rw-r--r--3rdparty/imgui/source/imgui.cpp6389
-rw-r--r--3rdparty/imgui/source/imgui.h414
-rw-r--r--3rdparty/imgui/source/imgui_demo.cpp357
-rw-r--r--3rdparty/imgui/source/imgui_draw.cpp96
-rw-r--r--3rdparty/imgui/source/imgui_internal.h467
-rw-r--r--3rdparty/imgui/source/imgui_tables.cpp31
-rw-r--r--3rdparty/imgui/source/imgui_widgets.cpp469
-rw-r--r--3rdparty/imgui/source/imstb_rectpack.h42
-rw-r--r--3rdparty/imgui/source/imstb_textedit.h14
-rw-r--r--3rdparty/imgui/source/imstb_truetype.h778
18 files changed, 8655 insertions, 1092 deletions
diff --git a/3rdparty/imgui/README.md b/3rdparty/imgui/README.md
index 803ac03..9067e0b 100644
--- a/3rdparty/imgui/README.md
+++ b/3rdparty/imgui/README.md
@@ -1,5 +1,5 @@
## Source
-Version: v1.87
-https://github.com/ocornut/imgui/commit/c71a50deb5ddf1ea386b91e60fa2e4a26d080074
+Version: `docking` branch
+https://github.com/ocornut/imgui/
+ Only relevant sources are here
+ Applied patch to expose more internal APIs (see each file)
diff --git a/3rdparty/imgui/source/backends/imgui_impl_glfw.cpp b/3rdparty/imgui/source/backends/imgui_impl_glfw.cpp
index 68f1188..02706c7 100644
--- a/3rdparty/imgui/source/backends/imgui_impl_glfw.cpp
+++ b/3rdparty/imgui/source/backends/imgui_impl_glfw.cpp
@@ -1,13 +1,17 @@
// dear imgui: Platform Backend for GLFW
// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..)
// (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.
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+).
+// [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 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.
@@ -16,7 +20,9 @@
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
-// 2022-02-07: Added ImGui_ImplGlfw_InstallCallbacks()/ImGui_ImplGlfw_RestoreCallbacks() helpers to facilitate user installing callbacks after iniitializing backend.
+// 2022-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
+// 2022-03-23: Inputs: Fixed a regression in 1.87 which resulted in keyboard modifiers events being reported incorrectly on Linux/X11.
+// 2022-02-07: Added ImGui_ImplGlfw_InstallCallbacks()/ImGui_ImplGlfw_RestoreCallbacks() helpers to facilitate user installing callbacks after initializing backend.
// 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[].
// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+).
@@ -69,11 +75,25 @@
#define GLFW_EXPOSE_NATIVE_WIN32
#include <GLFW/glfw3native.h> // 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_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
#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
#define GLFW_HAS_GAMEPAD_API (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetGamepadState() new api
#define GLFW_HAS_GET_KEY_NAME (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwGetKeyName()
@@ -93,7 +113,9 @@ struct ImGui_ImplGlfw_Data
GLFWwindow* MouseWindow;
GLFWcursor* MouseCursors[ImGuiMouseCursor_COUNT];
ImVec2 LastValidMousePos;
+ GLFWwindow* KeyOwnerWindows[GLFW_KEY_LAST];
bool InstalledCallbacks;
+ bool WantUpdateMonitors;
// Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any.
GLFWwindowfocusfun PrevUserCallbackWindowFocus;
@@ -105,7 +127,7 @@ struct ImGui_ImplGlfw_Data
GLFWcharfun PrevUserCallbackChar;
GLFWmonitorfun PrevUserCallbackMonitor;
- ImGui_ImplGlfw_Data() { memset(this, 0, sizeof(*this)); }
+ ImGui_ImplGlfw_Data() { memset((void*)this, 0, sizeof(*this)); }
};
// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts
@@ -120,6 +142,11 @@ static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData()
return ImGui::GetCurrentContext() ? (ImGui_ImplGlfw_Data*)ImGui::GetIO().BackendPlatformUserData : NULL;
}
+// Forward Declarations
+static void ImGui_ImplGlfw_UpdateMonitors();
+static void ImGui_ImplGlfw_InitPlatformInterface();
+static void ImGui_ImplGlfw_ShutdownPlatformInterface();
+
// Functions
static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data)
{
@@ -131,6 +158,7 @@ static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text)
glfwSetClipboardString((GLFWwindow*)user_data, text);
}
+// CUSTOM ADDITIONS
ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int key)
{
switch (key)
@@ -244,6 +272,19 @@ ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int key)
}
}
+static int ImGui_ImplGlfw_KeyToModifier(int key)
+{
+ if (key == GLFW_KEY_LEFT_CONTROL || key == GLFW_KEY_RIGHT_CONTROL)
+ return GLFW_MOD_CONTROL;
+ if (key == GLFW_KEY_LEFT_SHIFT || key == GLFW_KEY_RIGHT_SHIFT)
+ return GLFW_MOD_SHIFT;
+ if (key == GLFW_KEY_LEFT_ALT || key == GLFW_KEY_RIGHT_ALT)
+ return GLFW_MOD_ALT;
+ if (key == GLFW_KEY_LEFT_SUPER || key == GLFW_KEY_RIGHT_SUPER)
+ return GLFW_MOD_SUPER;
+ return 0;
+}
+
static void ImGui_ImplGlfw_UpdateKeyModifiers(int mods)
{
ImGuiIO& io = ImGui::GetIO();
@@ -312,8 +353,14 @@ void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int keycode, int scancode, i
if (action != GLFW_PRESS && action != GLFW_RELEASE)
return;
+ // Workaround: X11 does not include current pressed/released modifier key in 'mods' flags. https://github.com/glfw/glfw/issues/1630
+ if (int keycode_to_mod = ImGui_ImplGlfw_KeyToModifier(keycode))
+ mods = (action == GLFW_PRESS) ? (mods | keycode_to_mod) : (mods & ~keycode_to_mod);
ImGui_ImplGlfw_UpdateKeyModifiers(mods);
+ if (keycode >= 0 && keycode < IM_ARRAYSIZE(bd->KeyOwnerWindows))
+ bd->KeyOwnerWindows[keycode] = (action == GLFW_PRESS) ? window : NULL;
+
keycode = ImGui_ImplGlfw_TranslateUntranslatedKey(keycode, scancode);
ImGuiIO& io = ImGui::GetIO();
@@ -339,6 +386,13 @@ void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y)
bd->PrevUserCallbackCursorPos(window, x, y);
ImGuiIO& io = ImGui::GetIO();
+ if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
+ {
+ int window_x, window_y;
+ glfwGetWindowPos(window, &window_x, &window_y);
+ x += window_x;
+ y += window_y;
+ }
io.AddMousePosEvent((float)x, (float)y);
bd->LastValidMousePos = ImVec2((float)x, (float)y);
}
@@ -377,7 +431,8 @@ void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c)
void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int)
{
- // Unused in 'master' branch but 'docking' branch will use this, so we declare it ahead of it so if you have to install callbacks you can install this one too.
+ ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
+ bd->WantUpdateMonitors = true;
}
void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window)
@@ -433,19 +488,19 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
io.BackendPlatformName = "imgui_impl_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_MOUSE_PASSTHROUGH || (GLFW_HAS_WINDOW_HOVERED && defined(_WIN32))
+ io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional)
+#endif
bd->Window = window;
bd->Time = 0.0;
+ bd->WantUpdateMonitors = true;
io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText;
io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText;
io.ClipboardUserData = bd->Window;
- // Set platform dependent data in viewport
-#if defined(_WIN32)
- ImGui::GetMainViewport()->PlatformHandleRaw = (void*)glfwGetWin32Window(bd->Window);
-#endif
-
// Create mouse cursors
// (By design, on X11 cursors are user configurable and some cursors may be missing. When a cursor doesn't exist,
// GLFW will emit an error which will often be printed by the app, so we temporarily disable error reporting.
@@ -473,6 +528,19 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
if (install_callbacks)
ImGui_ImplGlfw_InstallCallbacks(window);
+ // 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*)bd->Window;
+#ifdef _WIN32
+ main_viewport->PlatformHandleRaw = glfwGetWin32Window(bd->Window);
+#endif
+ if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
+ ImGui_ImplGlfw_InitPlatformInterface();
+
bd->ClientApi = client_api;
return true;
}
@@ -498,6 +566,8 @@ void ImGui_ImplGlfw_Shutdown()
IM_ASSERT(bd != NULL && "No platform backend to shutdown, or already shutdown?");
ImGuiIO& io = ImGui::GetIO();
+ ImGui_ImplGlfw_ShutdownPlatformInterface();
+
if (bd->InstalledCallbacks)
ImGui_ImplGlfw_RestoreCallbacks(bd->Window);
@@ -513,27 +583,70 @@ static void ImGui_ImplGlfw_UpdateMouseData()
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
ImGuiIO& io = ImGui::GetIO();
+ ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
+
+ ImGuiID mouse_viewport_id = 0;
+ const ImVec2 mouse_pos_prev = io.MousePos;
+ for (int n = 0; n < platform_io.Viewports.Size; n++)
+ {
+ ImGuiViewport* viewport = platform_io.Viewports[n];
+ GLFWwindow* window = (GLFWwindow*)viewport->PlatformHandle;
#ifdef __EMSCRIPTEN__
- const bool is_app_focused = true;
+ const bool is_window_focused = true;
#else
- const bool is_app_focused = glfwGetWindowAttrib(bd->Window, GLFW_FOCUSED) != 0;
+ const bool is_window_focused = glfwGetWindowAttrib(window, GLFW_FOCUSED) != 0;
#endif
- if (is_app_focused)
- {
- // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
- if (io.WantSetMousePos)
- glfwSetCursorPos(bd->Window, (double)io.MousePos.x, (double)io.MousePos.y);
-
- // (Optional) Fallback to provide mouse position when focused (ImGui_ImplGlfw_CursorPosCallback already provides this when hovered or captured)
- if (is_app_focused && bd->MouseWindow == NULL)
+ if (is_window_focused)
{
- double mouse_x, mouse_y;
- glfwGetCursorPos(bd->Window, &mouse_x, &mouse_y);
- io.AddMousePosEvent((float)mouse_x, (float)mouse_y);
- bd->LastValidMousePos = ImVec2((float)mouse_x, (float)mouse_y);
+ // (Optional) 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)
+ glfwSetCursorPos(window, (double)(mouse_pos_prev.x - viewport->Pos.x), (double)(mouse_pos_prev.y - viewport->Pos.y));
+
+ // (Optional) Fallback to provide mouse position when focused (ImGui_ImplGlfw_CursorPosCallback already provides this when hovered or captured)
+ if (bd->MouseWindow == NULL)
+ {
+ double mouse_x, mouse_y;
+ glfwGetCursorPos(window, &mouse_x, &mouse_y);
+ if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
+ {
+ // 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)
+ // 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);
+ mouse_x += window_x;
+ mouse_y += window_y;
+ }
+ bd->LastValidMousePos = ImVec2((float)mouse_x, (float)mouse_y);
+ io.AddMousePosEvent((float)mouse_x, (float)mouse_y);
+ }
}
+
+ // (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.
+ // 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;
+#if GLFW_HAS_MOUSE_PASSTHROUGH
+ glfwSetWindowAttrib(window, GLFW_MOUSE_PASSTHROUGH, window_no_input);
+#endif
+ if (glfwGetWindowAttrib(window, GLFW_HOVERED) && !window_no_input)
+ 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()
@@ -544,17 +657,22 @@ static void ImGui_ImplGlfw_UpdateMouseCursor()
return;
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
- if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
+ ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
+ for (int n = 0; n < platform_io.Viewports.Size; n++)
{
- // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
- glfwSetInputMode(bd->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(bd->Window, bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]);
- glfwSetInputMode(bd->Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
+ GLFWwindow* window = (GLFWwindow*)platform_io.Viewports[n]->PlatformHandle;
+ if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
+ {
+ // 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, bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]);
+ glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
+ }
}
}
@@ -611,6 +729,41 @@ static void ImGui_ImplGlfw_UpdateGamepads()
#undef MAP_ANALOG
}
+static void ImGui_ImplGlfw_UpdateMonitors()
+{
+ ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
+ 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]);
+ 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
+ int w, h;
+ glfwGetMonitorWorkarea(glfw_monitors[n], &x, &y, &w, &h);
+ 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.
+ float x_scale, y_scale;
+ glfwGetMonitorContentScale(glfw_monitors[n], &x_scale, &y_scale);
+ monitor.DpiScale = x_scale;
+#endif
+ platform_io.Monitors.push_back(monitor);
+ }
+ bd->WantUpdateMonitors = false;
+}
+
void ImGui_ImplGlfw_NewFrame()
{
ImGuiIO& io = ImGui::GetIO();
@@ -625,6 +778,8 @@ void ImGui_ImplGlfw_NewFrame()
io.DisplaySize = ImVec2((float)w, (float)h);
if (w > 0 && h > 0)
io.DisplayFramebufferScale = ImVec2((float)display_w / (float)w, (float)display_h / (float)h);
+ if (bd->WantUpdateMonitors)
+ ImGui_ImplGlfw_UpdateMonitors();
// Setup time step
double current_time = glfwGetTime();
@@ -638,6 +793,361 @@ void ImGui_ImplGlfw_NewFrame()
ImGui_ImplGlfw_UpdateGamepads();
}
+//--------------------------------------------------------------------------------------------------------
+// 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..
+//--------------------------------------------------------------------------------------------------------
+
+// Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data.
+struct ImGui_ImplGlfw_ViewportData
+{
+ GLFWwindow* Window;
+ bool WindowOwned;
+ int IgnoreWindowPosEventFrame;
+ int IgnoreWindowSizeEventFrame;
+
+ ImGui_ImplGlfw_ViewportData() { Window = NULL; WindowOwned = false; IgnoreWindowSizeEventFrame = IgnoreWindowPosEventFrame = -1; }
+ ~ImGui_ImplGlfw_ViewportData() { IM_ASSERT(Window == NULL); }
+};
+
+static void ImGui_ImplGlfw_WindowCloseCallback(GLFWwindow* window)
+{
+ if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(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 (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData)
+ {
+ bool ignore_event = (ImGui::GetFrameCount() <= vd->IgnoreWindowPosEventFrame + 1);
+ //data->IgnoreWindowPosEventFrame = -1;
+ if (ignore_event)
+ return;
+ }
+ viewport->PlatformRequestMove = true;
+ }
+}
+
+static void ImGui_ImplGlfw_WindowSizeCallback(GLFWwindow* window, int, int)
+{
+ if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window))
+ {
+ if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData)
+ {
+ bool ignore_event = (ImGui::GetFrameCount() <= vd->IgnoreWindowSizeEventFrame + 1);
+ //data->IgnoreWindowSizeEventFrame = -1;
+ if (ignore_event)
+ return;
+ }
+ viewport->PlatformRequestResize = true;
+ }
+}
+
+static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport)
+{
+ ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
+ 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
+ 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);
+#endif
+ GLFWwindow* share_window = (bd->ClientApi == GlfwClientApi_OpenGL) ? bd->Window : NULL;
+ 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(vd->Window);
+#endif
+ glfwSetWindowPos(vd->Window, (int)viewport->Pos.x, (int)viewport->Pos.y);
+
+ // Install GLFW callbacks for secondary viewports
+ glfwSetWindowFocusCallback(vd->Window, ImGui_ImplGlfw_WindowFocusCallback);
+ glfwSetCursorEnterCallback(vd->Window, ImGui_ImplGlfw_CursorEnterCallback);
+ glfwSetCursorPosCallback(vd->Window, ImGui_ImplGlfw_CursorPosCallback);
+ 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(vd->Window);
+ glfwSwapInterval(0);
+ }
+}
+
+static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport)
+{
+ ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
+ if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData)
+ {
+ if (vd->WindowOwned)
+ {
+#if !GLFW_HAS_MOUSE_PASSTHROUGH && GLFW_HAS_WINDOW_HOVERED && defined(_WIN32)
+ 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(bd->KeyOwnerWindows); i++)
+ 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(vd->Window);
+ }
+ vd->Window = NULL;
+ IM_DELETE(vd);
+ }
+ viewport->PlatformUserData = viewport->PlatformHandle = NULL;
+}
+
+// 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)
+{
+ if (msg == WM_NCHITTEST)
+ {
+ // 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.
+ 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)
+{
+ ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData;
+
+#if defined(_WIN32)
+ // GLFW hack: Hide icon from task bar
+ HWND hwnd = (HWND)viewport->PlatformHandleRaw;
+ 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);
+ }
+
+ // GLFW hack: install hook for WM_NCHITTEST message handler
+#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);
+ ::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
+ // FIXME-VIEWPORT: Implement same work-around for Linux/OSX in the meanwhile.
+ if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing)
+ {
+ ::ShowWindow(hwnd, SW_SHOWNA);
+ return;
+ }
+#endif
+#endif
+
+ glfwShowWindow(vd->Window);
+}
+
+static ImVec2 ImGui_ImplGlfw_GetWindowPos(ImGuiViewport* viewport)
+{
+ ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData;
+ int x = 0, y = 0;
+ glfwGetWindowPos(vd->Window, &x, &y);
+ return ImVec2((float)x, (float)y);
+}
+
+static void ImGui_ImplGlfw_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos)
+{
+ 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)
+{
+ ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData;
+ int w = 0, h = 0;
+ glfwGetWindowSize(vd->Window, &w, &h);
+ return ImVec2((float)w, (float)h);
+}
+
+static void ImGui_ImplGlfw_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
+{
+ 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(vd->Window, &x, &y);
+ glfwGetWindowSize(vd->Window, &width, &height);
+ glfwSetWindowPos(vd->Window, x, y - height + size.y);
+#endif
+ vd->IgnoreWindowSizeEventFrame = ImGui::GetFrameCount();
+ glfwSetWindowSize(vd->Window, (int)size.x, (int)size.y);
+}
+
+static void ImGui_ImplGlfw_SetWindowTitle(ImGuiViewport* viewport, const char* 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
+ 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;
+#endif
+}
+
+static bool ImGui_ImplGlfw_GetWindowFocus(ImGuiViewport* viewport)
+{
+ ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData;
+ return glfwGetWindowAttrib(vd->Window, GLFW_FOCUSED) != 0;
+}
+
+static bool ImGui_ImplGlfw_GetWindowMinimized(ImGuiViewport* viewport)
+{
+ 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)
+{
+ 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();
+ ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData;
+ if (bd->ClientApi == GlfwClientApi_OpenGL)
+ glfwMakeContextCurrent(vd->Window);
+}
+
+static void ImGui_ImplGlfw_SwapBuffers(ImGuiViewport* viewport, void*)
+{
+ ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
+ ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData;
+ if (bd->ClientApi == GlfwClientApi_OpenGL)
+ {
+ glfwMakeContextCurrent(vd->Window);
+ glfwSwapBuffers(vd->Window);
+ }
+}
+
+//--------------------------------------------------------------------------------------------------------
+// Vulkan support (the Vulkan renderer needs to call a platform-side support function to create the surface)
+//--------------------------------------------------------------------------------------------------------
+
+// Avoid including <vulkan.h> 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)
+{
+ 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;
+}
+#endif // GLFW_HAS_VULKAN
+
+static void ImGui_ImplGlfw_InitPlatformInterface()
+{
+ // Register platform interface (will be coupled with a renderer interface)
+ ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
+ 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_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;
+#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
+
+ // 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();
+ 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;
+}
+
+static void ImGui_ImplGlfw_ShutdownPlatformInterface()
+{
+ ImGui::DestroyPlatformWindows();
+}
+
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
diff --git a/3rdparty/imgui/source/backends/imgui_impl_glfw.h b/3rdparty/imgui/source/backends/imgui_impl_glfw.h
index 86811ef..13b3896 100644
--- a/3rdparty/imgui/source/backends/imgui_impl_glfw.h
+++ b/3rdparty/imgui/source/backends/imgui_impl_glfw.h
@@ -1,12 +1,17 @@
// dear imgui: Platform Backend for GLFW
// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..)
// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.)
+// (Requires: GLFW 3.1+. Prefer GLFW 3.3+ for full feature support.)
// Implemented features:
// [X] Platform: Clipboard support.
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
-// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+).
+// [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+).
+// [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 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/3rdparty/imgui/source/backends/imgui_impl_opengl2.cpp b/3rdparty/imgui/source/backends/imgui_impl_opengl2.cpp
index 17a6fae..6f906d2 100644
--- a/3rdparty/imgui/source/backends/imgui_impl_opengl2.cpp
+++ b/3rdparty/imgui/source/backends/imgui_impl_opengl2.cpp
@@ -3,6 +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'.
// 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.
@@ -19,6 +20,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.
// 2021-12-08: OpenGL: 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-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-05-19: OpenGL: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
@@ -61,7 +63,7 @@ struct ImGui_ImplOpenGL2_Data
{
GLuint FontTexture;
- ImGui_ImplOpenGL2_Data() { memset(this, 0, sizeof(*this)); }
+ ImGui_ImplOpenGL2_Data() { memset((void*)this, 0, sizeof(*this)); }
};
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
@@ -71,6 +73,10 @@ static ImGui_ImplOpenGL2_Data* ImGui_ImplOpenGL2_GetBackendData()
return ImGui::GetCurrentContext() ? (ImGui_ImplOpenGL2_Data*)ImGui::GetIO().BackendRendererUserData : NULL;
}
+// Forward Declarations
+static void ImGui_ImplOpenGL2_InitPlatformInterface();
+static void ImGui_ImplOpenGL2_ShutdownPlatformInterface();
+
// Functions
bool ImGui_ImplOpenGL2_Init()
{
@@ -81,6 +87,10 @@ bool ImGui_ImplOpenGL2_Init()
ImGui_ImplOpenGL2_Data* bd = IM_NEW(ImGui_ImplOpenGL2_Data)();
io.BackendRendererUserData = (void*)bd;
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;
}
@@ -91,6 +101,7 @@ void ImGui_ImplOpenGL2_Shutdown()
IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?");
ImGuiIO& io = ImGui::GetIO();
+ ImGui_ImplOpenGL2_ShutdownPlatformInterface();
ImGui_ImplOpenGL2_DestroyDeviceObjects();
io.BackendRendererName = NULL;
io.BackendRendererUserData = NULL;
@@ -244,6 +255,7 @@ bool ImGui_ImplOpenGL2_CreateFontsTexture()
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (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
+ // (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);
@@ -283,3 +295,31 @@ void ImGui_ImplOpenGL2_DestroyDeviceObjects()
{
ImGui_ImplOpenGL2_DestroyFontsTexture();
}
+
+//--------------------------------------------------------------------------------------------------------
+// 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_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/3rdparty/imgui/source/backends/imgui_impl_opengl2.h b/3rdparty/imgui/source/backends/imgui_impl_opengl2.h
index d00d27f..780204a 100644
--- a/3rdparty/imgui/source/backends/imgui_impl_opengl2.h
+++ b/3rdparty/imgui/source/backends/imgui_impl_opengl2.h
@@ -3,6 +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'.
// 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/3rdparty/imgui/source/backends/imgui_impl_opengl3.cpp b/3rdparty/imgui/source/backends/imgui_impl_opengl3.cpp
index 93f057d..1c8425b 100644
--- a/3rdparty/imgui/source/backends/imgui_impl_opengl3.cpp
+++ b/3rdparty/imgui/source/backends/imgui_impl_opengl3.cpp
@@ -9,6 +9,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: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
@@ -18,6 +19,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.
// 2021-12-15: OpenGL: Using buffer orphaning + glBufferSubData(), seems to fix leaks with multi-viewports with some Intel HD drivers.
// 2021-08-23: OpenGL: Fixed ES 3.0 shader ("#version 300 es") use normal precision floats to avoid wobbly rendering at HD resolutions.
// 2021-08-19: OpenGL: Embed and use our own minimal GL loader (imgui_impl_opengl3_loader.h), removing requirement and support for third-party loader.
@@ -197,7 +199,7 @@ struct ImGui_ImplOpenGL3_Data
GLsizeiptr IndexBufferSize;
bool HasClipOrigin;
- ImGui_ImplOpenGL3_Data() { memset(this, 0, sizeof(*this)); }
+ ImGui_ImplOpenGL3_Data() { memset((void*)this, 0, sizeof(*this)); }
};
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
@@ -207,6 +209,10 @@ static ImGui_ImplOpenGL3_Data* ImGui_ImplOpenGL3_GetBackendData()
return ImGui::GetCurrentContext() ? (ImGui_ImplOpenGL3_Data*)ImGui::GetIO().BackendRendererUserData : NULL;
}
+// Forward Declarations
+static void ImGui_ImplOpenGL3_InitPlatformInterface();
+static void ImGui_ImplOpenGL3_ShutdownPlatformInterface();
+
// Functions
bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
{
@@ -248,6 +254,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
if (bd->GlVersion >= 320)
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.
@@ -285,6 +292,9 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
}
#endif
+ if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
+ ImGui_ImplOpenGL3_InitPlatformInterface();
+
return true;
}
@@ -294,6 +304,7 @@ void ImGui_ImplOpenGL3_Shutdown()
IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?");
ImGuiIO& io = ImGui::GetIO();
+ ImGui_ImplOpenGL3_ShutdownPlatformInterface();
ImGui_ImplOpenGL3_DestroyDeviceObjects();
io.BackendRendererName = NULL;
io.BackendRendererUserData = NULL;
@@ -543,6 +554,7 @@ bool ImGui_ImplOpenGL3_CreateFontsTexture()
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (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
+ // (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);
@@ -810,6 +822,34 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects()
ImGui_ImplOpenGL3_DestroyFontsTexture();
}
+//--------------------------------------------------------------------------------------------------------
+// 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_ImplOpenGL3_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_ImplOpenGL3_RenderDrawData(viewport->DrawData);
+}
+
+static void ImGui_ImplOpenGL3_InitPlatformInterface()
+{
+ ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
+ platform_io.Renderer_RenderWindow = ImGui_ImplOpenGL3_RenderWindow;
+}
+
+static void ImGui_ImplOpenGL3_ShutdownPlatformInterface()
+{
+ ImGui::DestroyPlatformWindows();
+}
+
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
diff --git a/3rdparty/imgui/source/backends/imgui_impl_opengl3.h b/3rdparty/imgui/source/backends/imgui_impl_opengl3.h
index 98c9aca..e7f652f 100644
--- a/3rdparty/imgui/source/backends/imgui_impl_opengl3.h
+++ b/3rdparty/imgui/source/backends/imgui_impl_opengl3.h
@@ -5,6 +5,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: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
@@ -46,7 +47,7 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
#endif
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__))
#define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es"
-#elif defined(__EMSCRIPTEN__)
+#elif defined(__EMSCRIPTEN__) || defined(__amigaos4__)
#define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100"
#else
// Otherwise imgui_impl_opengl3_loader.h will be used.
diff --git a/3rdparty/imgui/source/imconfig.h b/3rdparty/imgui/source/imconfig.h
index 774b8f0..9a2aca4 100644
--- a/3rdparty/imgui/source/imconfig.h
+++ b/3rdparty/imgui/source/imconfig.h
@@ -62,12 +62,13 @@
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
+//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if enabled
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
-//---- Use stb_printf's faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
-// Requires 'stb_sprintf.h' to be available in the include path. Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by STB sprintf.
-// #define IMGUI_USE_STB_SPRINTF
+//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
+// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.
+//#define IMGUI_USE_STB_SPRINTF
//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).
@@ -81,12 +82,12 @@
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
/*
-#define IM_VEC2_CLASS_EXTRA \
- ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \
+#define IM_VEC2_CLASS_EXTRA \
+ constexpr ImVec2(const MyVec2& f) : x(f.x), y(f.y) {} \
operator MyVec2() const { return MyVec2(x,y); }
-#define IM_VEC4_CLASS_EXTRA \
- ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \
+#define IM_VEC4_CLASS_EXTRA \
+ constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {} \
operator MyVec4() const { return MyVec4(x,y,z,w); }
*/
diff --git a/3rdparty/imgui/source/imgui.cpp b/3rdparty/imgui/source/imgui.cpp
index efa6960..69e63e6 100644
--- a/3rdparty/imgui/source/imgui.cpp
+++ b/3rdparty/imgui/source/imgui.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.87
+// dear imgui, 1.88 WIP
// (main code and documentation)
// Help:
@@ -80,7 +80,8 @@ CODE
// [SECTION] DRAG AND DROP
// [SECTION] LOGGING/CAPTURING
// [SECTION] SETTINGS
-// [SECTION] VIEWPORTS
+// [SECTION] VIEWPORTS, PLATFORM WINDOWS
+// [SECTION] DOCKING
// [SECTION] PLATFORM DEPENDENT HELPERS
// [SECTION] METRICS/DEBUGGER WINDOW
// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL)
@@ -101,6 +102,7 @@ CODE
- Easy to hack and improve.
- Minimize setup and maintenance.
- Minimize state storage on user side.
+ - Minimize state synchronization.
- Portable, minimize dependencies, run on target (consoles, phones, etc.).
- Efficient runtime and memory consumption.
@@ -291,6 +293,7 @@ CODE
void void MyImGuiRenderFunction(ImDrawData* draw_data)
{
// TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
+ // TODO: Setup texture sampling state: sample with bilinear filtering (NOT point/nearest filtering). Use 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines;' to allow point/nearest filtering.
// TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
// TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
// TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
@@ -384,6 +387,14 @@ CODE
When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
You can read releases logs https://github.com/ocornut/imgui/releases for more details.
+(Docking/Viewport Branch)
+ - 2021/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.
+ If you query mouse positions to interact with non-imgui coordinates you will need to offset them, e.g. subtract GetWindowViewport()->Pos.
+
+ - 2022/04/05 (1.88) - inputs: renamed ImGuiKeyModFlags to ImGuiModFlags. Kept inline redirection enums (will obsolete). This was never used in public API functions but technically present in imgui.h and ImGuiIO.
- 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).
@@ -392,6 +403,7 @@ CODE
- Backend writing to io.MouseDown[] -> backend should call io.AddMouseButtonEvent()
- Backend writing to io.MouseWheel -> backend should call io.AddMouseWheelEvent()
- Backend writing to io.MouseHoveredViewport -> backend should call io.AddMouseViewportEvent() [Docking branch w/ multi-viewports only]
+ note: for all calls to IO new functions, the Dear ImGui context should be bound/current.
- 2022/01/10 (1.87) - inputs: reworked keyboard IO. Removed io.KeyMap[], io.KeysDown[] in favor of calling io.AddKeyEvent(). Removed GetKeyIndex(), now unecessary. All IsKeyXXX() functions now take ImGuiKey values. All features are still functional until IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Read Changelog and Release Notes for details.
- IsKeyPressed(MY_NATIVE_KEY_XXX) -> use IsKeyPressed(ImGuiKey_XXX)
- IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX)
@@ -912,6 +924,10 @@ static const float WINDOWS_HOVER_PADDING = 4.0f; // Exten
static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time.
static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 2.00f; // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved.
+// 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
//-------------------------------------------------------------------------
@@ -973,14 +989,26 @@ static void UpdateMouseInputs();
static void UpdateMouseWheel();
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, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size);
+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 RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col);
static void RenderDimmedBackgrounds();
static ImGuiWindow* FindBlockingModal(ImGuiWindow* window);
// 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);
+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);
+static int FindPlatformMonitorForPos(const ImVec2& pos);
+static int FindPlatformMonitorForRect(const ImRect& r);
+static void UpdateViewportPlatformMonitor(ImGuiViewportP* viewport);
}
@@ -1068,7 +1096,7 @@ ImGuiStyle::ImGuiStyle()
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-aliased lines/borders. Disable if you are really tight on CPU/GPU.
- AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering.
+ AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering).
AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).
CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
CircleTessellationMaxError = 0.30f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
@@ -1136,6 +1164,18 @@ ImGuiIO::ImGuiIO()
FontAllowUserScaling = false;
DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
+ // Docking options (when ImGuiConfigFlags_DockingEnable is set)
+ ConfigDockingNoSplit = false;
+ ConfigDockingWithShift = false;
+ ConfigDockingAlwaysTabBar = false;
+ ConfigDockingTransparentPayload = false;
+
+ // Viewport options (when ImGuiConfigFlags_ViewportsEnable is set)
+ ConfigViewportsNoAutoMerge = false;
+ ConfigViewportsNoTaskBarIcon = false;
+ ConfigViewportsNoDecoration = true;
+ ConfigViewportsNoDefaultParent = false;
+
// Miscellaneous options
MouseDrawCursor = false;
#ifdef __APPLE__
@@ -1180,7 +1220,7 @@ void ImGuiIO::AddInputCharacter(unsigned int c)
return;
ImGuiInputEvent e;
- e.Type = ImGuiInputEventType_Char;
+ e.Type = ImGuiInputEventType_Text;
e.Source = ImGuiInputSource_Keyboard;
e.Text.Char = c;
g.InputEventsQueue.push_back(e);
@@ -1250,7 +1290,7 @@ void ImGuiIO::ClearInputKeys()
KeysData[n].DownDurationPrev = -1.0f;
}
KeyCtrl = KeyShift = KeyAlt = KeySuper = false;
- KeyMods = KeyModsPrev = ImGuiKeyModFlags_None;
+ KeyMods = ImGuiModFlags_None;
for (int n = 0; n < IM_ARRAYSIZE(NavInputsDownDuration); n++)
NavInputsDownDuration[n] = NavInputsDownDurationPrev[n] = -1.0f;
}
@@ -1321,8 +1361,10 @@ void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native
// Build native->imgui map so old user code can still call key functions with native 0..511 values.
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
const int legacy_key = (native_legacy_index != -1) ? native_legacy_index : native_keycode;
- if (ImGui::IsLegacyKey(legacy_key))
- KeyMap[legacy_key] = key;
+ if (!ImGui::IsLegacyKey(legacy_key))
+ return;
+ KeyMap[legacy_key] = key;
+ KeyMap[key] = legacy_key;
#else
IM_UNUSED(key);
IM_UNUSED(native_legacy_index);
@@ -1373,6 +1415,19 @@ void ImGuiIO::AddMouseWheelEvent(float wheel_x, float wheel_y)
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;
@@ -1637,8 +1692,12 @@ const char* ImStrSkipBlank(const char* str)
// designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)
#ifdef IMGUI_USE_STB_SPRINTF
#define STB_SPRINTF_IMPLEMENTATION
+#ifdef IMGUI_STB_SPRINTF_FILENAME
+#include IMGUI_STB_SPRINTF_FILENAME
+#else
#include "stb_sprintf.h"
#endif
+#endif
#if defined(_MSC_VER) && !defined(vsnprintf)
#define vsnprintf _vsnprintf
@@ -2239,18 +2298,15 @@ void ImGuiStorage::SetAllInt(int v)
//-----------------------------------------------------------------------------
// Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
-ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
+ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) //-V1077
{
+ InputBuf[0] = 0;
+ CountGrep = 0;
if (default_filter)
{
ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
Build();
}
- else
- {
- InputBuf[0] = 0;
- CountGrep = 0;
- }
}
bool ImGuiTextFilter::Draw(const char* label, float width)
@@ -2565,15 +2621,14 @@ void ImGuiListClipper::Begin(int items_count, float items_height)
void ImGuiListClipper::End()
{
- // In theory here we should assert that we are already at the right position, but it seems saner to just seek at the end and not assert/crash the user.
ImGuiContext& g = *GImGui;
- if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0)
- ImGuiListClipper_SeekCursorForItem(this, ItemsCount);
- ItemsCount = -1;
-
- // Restore temporary buffer and fix back pointers which may be invalidated when nesting
if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData)
{
+ // In theory here we should assert that we are already at the right position, but it seems saner to just seek at the end and not assert/crash the user.
+ if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0)
+ ImGuiListClipper_SeekCursorForItem(this, ItemsCount);
+
+ // Restore temporary buffer and fix back pointers which may be invalidated when nesting
IM_ASSERT(data->ListClipper == this);
data->StepNo = data->Ranges.Size;
if (--g.ClipperTempDataStacked > 0)
@@ -2583,6 +2638,7 @@ void ImGuiListClipper::End()
}
TempData = NULL;
}
+ ItemsCount = -1;
}
void ImGuiListClipper::ForceDisplayRangeByIndices(int item_min, int item_max)
@@ -2599,6 +2655,7 @@ bool ImGuiListClipper::Step()
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
ImGuiListClipperData* data = (ImGuiListClipperData*)TempData;
+ IM_ASSERT(data != NULL && "Called ImGuiListClipper::Step() too many times, or before ImGuiListClipper::Begin() ?");
ImGuiTable* table = g.CurrentTable;
if (table && table->IsInsideRow)
@@ -2715,8 +2772,8 @@ bool ImGuiListClipper::Step()
// Advance the cursor to the end of the list and then returns 'false' to end the loop.
if (ItemsCount < INT_MAX)
ImGuiListClipper_SeekCursorForItem(this, ItemsCount);
- ItemsCount = -1;
+ End();
return false;
}
@@ -2803,6 +2860,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
@@ -2926,6 +2988,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_DockingEmptyBg: return "DockingEmptyBg";
case ImGuiCol_PlotLines: return "PlotLines";
case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
case ImGuiCol_PlotHistogram: return "PlotHistogram";
@@ -3193,6 +3257,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!)
//-----------------------------------------------------------------------------
@@ -3205,20 +3297,26 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) : DrawListInst
NameBufLen = (int)strlen(name) + 1;
ID = ImHashStr(name);
IDStack.push_back(ID);
+ 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;
AutoPosLastDirection = ImGuiDir_None;
- 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;
+ LastFrameJustFocused = -1;
LastTimeActive = -1.0f;
- FontWindowScale = 1.0f;
+ FontWindowScale = FontDpiScale = 1.0f;
SettingsOffset = -1;
+ DockOrder = -1;
DrawList = &DrawListInst;
DrawList->_Data = &context->DrawListSharedData;
DrawList->_OwnerName = Name;
+ IM_PLACEMENT_NEW(&WindowClass) ImGuiWindowClass();
}
ImGuiWindow::~ImGuiWindow()
@@ -3232,7 +3330,6 @@ ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
{
ImGuiID seed = IDStack.back();
ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
- ImGui::KeepAliveID(id);
ImGuiContext& g = *GImGui;
if (g.DebugHookIdInfo == id)
ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
@@ -3243,7 +3340,6 @@ ImGuiID ImGuiWindow::GetID(const void* ptr)
{
ImGuiID seed = IDStack.back();
ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
- ImGui::KeepAliveID(id);
ImGuiContext& g = *GImGui;
if (g.DebugHookIdInfo == id)
ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL);
@@ -3254,37 +3350,6 @@ ImGuiID ImGuiWindow::GetID(int n)
{
ImGuiID seed = IDStack.back();
ImGuiID id = ImHashData(&n, sizeof(n), seed);
- ImGui::KeepAliveID(id);
- ImGuiContext& g = *GImGui;
- if (g.DebugHookIdInfo == id)
- ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
- return id;
-}
-
-ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
-{
- ImGuiID seed = IDStack.back();
- ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
- ImGuiContext& g = *GImGui;
- if (g.DebugHookIdInfo == id)
- ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
- return id;
-}
-
-ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
-{
- ImGuiID seed = IDStack.back();
- ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
- ImGuiContext& g = *GImGui;
- if (g.DebugHookIdInfo == id)
- ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL);
- return id;
-}
-
-ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n)
-{
- ImGuiID seed = IDStack.back();
- ImGuiID id = ImHashData(&n, sizeof(n), seed);
ImGuiContext& g = *GImGui;
if (g.DebugHookIdInfo == id)
ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
@@ -3297,7 +3362,6 @@ ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
ImGuiID seed = IDStack.back();
ImRect r_rel = ImGui::WindowRectAbsToRel(this, r_abs);
ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
- ImGui::KeepAliveID(id);
return id;
}
@@ -3400,6 +3464,8 @@ ImGuiID ImGui::GetHoveredID()
return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
}
+// This is called by ItemAdd().
+// Code not using ItemAdd() may need to call this manually otherwise ActiveId will be cleared. In IMGUI_VERSION_NUM < 18717 this was called by GetID().
void ImGui::KeepAliveID(ImGuiID id)
{
ImGuiContext& g = *GImGui;
@@ -3428,8 +3494,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.
@@ -3438,6 +3504,12 @@ static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFla
if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
return false;
}
+
+ // Filter by viewport
+ if (window->Viewport != g.MouseViewport)
+ if (g.MovingWindow == NULL || window->RootWindowDockTree != g.MovingWindow->RootWindowDockTree)
+ return false;
+
return true;
}
@@ -3448,7 +3520,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
- if (g.NavDisableMouseHover && !g.NavDisableHighlight)
+ if (g.NavDisableMouseHover && !g.NavDisableHighlight && !(flags & ImGuiHoveredFlags_NoNavOverride))
{
if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
return false;
@@ -3461,7 +3533,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags;
if (!(status_flags & ImGuiItemStatusFlags_HoveredRect))
return false;
- IM_ASSERT((flags & (ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy)) == 0); // Flags not supported by this function
+ IM_ASSERT((flags & (ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy | ImGuiHoveredFlags_DockHierarchy)) == 0); // Flags not supported by this function
// Test if we are hovering the right window (our window could be behind another window)
// [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851)
@@ -3474,8 +3546,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.
@@ -3487,7 +3560,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;
}
@@ -3509,8 +3583,6 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
return false;
if (!IsMouseHoveringRect(bb.Min, bb.Max))
return false;
- if (g.NavDisableMouseHover)
- return false;
if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
{
g.HoveredIdDisabled = true;
@@ -3546,6 +3618,9 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
IM_DEBUG_BREAK();
}
+ if (g.NavDisableMouseHover)
+ return false;
+
return true;
}
@@ -3663,20 +3738,23 @@ void ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeF
ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
{
+ ImGuiContext* prev_ctx = GetCurrentContext();
ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
- if (GImGui == NULL)
- SetCurrentContext(ctx);
- Initialize(ctx);
+ SetCurrentContext(ctx);
+ Initialize();
+ if (prev_ctx != NULL)
+ SetCurrentContext(prev_ctx); // Restore previous context if any, else keep new one.
return ctx;
}
void ImGui::DestroyContext(ImGuiContext* ctx)
{
- if (ctx == NULL)
- ctx = GImGui;
- Shutdown(ctx);
- if (GImGui == ctx)
- SetCurrentContext(NULL);
+ ImGuiContext* prev_ctx = GetCurrentContext();
+ if (ctx == NULL) //-V1051
+ ctx = prev_ctx;
+ SetCurrentContext(ctx);
+ Shutdown();
+ SetCurrentContext((prev_ctx != ctx) ? prev_ctx : NULL);
IM_DELETE(ctx);
}
@@ -3716,6 +3794,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;
+}
+
// Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame()
ImDrawData* ImGui::GetDrawData()
{
@@ -3766,7 +3850,7 @@ ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport)
ImDrawList* ImGui::GetBackgroundDrawList()
{
ImGuiContext& g = *GImGui;
- return GetBackgroundDrawList(g.Viewports[0]);
+ return GetBackgroundDrawList(g.CurrentWindow->Viewport);
}
ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport)
@@ -3777,7 +3861,7 @@ ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport)
ImDrawList* ImGui::GetForegroundDrawList()
{
ImGuiContext& g = *GImGui;
- return GetForegroundDrawList(g.Viewports[0]);
+ return GetForegroundDrawList(g.CurrentWindow->Viewport);
}
ImDrawListSharedData* ImGui::GetDrawListSharedData()
@@ -3794,17 +3878,46 @@ void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
FocusWindow(window);
SetActiveID(window->MoveId, window);
g.NavDisableHighlight = true;
- g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos;
+ g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindowDockTree->Pos;
g.ActiveIdNoClearOnFocusLoss = true;
SetActiveIdUsingNavAndKeys();
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))
+ can_move_window = false;
if (can_move_window)
g.MovingWindow = window;
}
+// 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_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) // -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;
+ }
+
+ const bool clicked = IsMouseClicked(0);
+ const bool dragging = IsMouseDragging(0, g.IO.MouseDragThreshold * 1.70f);
+ if (can_undock_node && dragging)
+ DockContextQueueUndockNode(&g, node); // Will lead to DockNodeStartMouseMovingWindow() -> StartMouseMovingWindow() being called next frame
+ else if (!can_undock_node && (clicked || dragging) && g.MovingWindow != window)
+ StartMouseMovingWindow(window);
+}
+
// Handle mouse moving window
// Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
// FIXME: We don't have strong guarantee that g.MovingWindow stay synched with g.ActiveId == g.MovingWindow->MoveId.
@@ -3818,20 +3931,43 @@ 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;
- if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
+ IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindowDockTree);
+ 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);
+ 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)
{
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);
}
else
{
+ 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;
+
+ // 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();
}
@@ -3861,7 +3997,7 @@ void ImGui::UpdateMouseMovingWindowEndFrame()
return;
// Click on empty space to focus window and start moving
- // (after we're done with all our widgets)
+ // (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])
{
// Handle the edge case of a popup being closed while clicking in its empty space.
@@ -3874,9 +4010,10 @@ void ImGui::UpdateMouseMovingWindowEndFrame()
StartMouseMovingWindow(g.HoveredWindow); //-V595
// Cancel moving if clicked outside of title bar
- if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(root_window->Flags & ImGuiWindowFlags_NoTitleBar))
- if (!root_window->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;
// Cancel moving if clicked over an item which was disabled or inhibited by popups (note that we know HoveredId == 0 already)
if (g.HoveredIdDisabled)
@@ -3902,6 +4039,29 @@ 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;
+ window->ClipRect.Translate(delta);
+ window->OuterRectClipped.Translate(delta);
+ window->InnerRect.Translate(delta);
+ window->DC.CursorPos += delta;
+ window->DC.CursorStartPos += delta;
+ window->DC.CursorMaxPos += delta;
+ window->DC.IdealMaxPos += 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->ContentSize = ImFloor(window->ContentSize * scale);
+}
+
static bool IsWindowActiveAndVisible(ImGuiWindow* window)
{
return (window->Active) && (!window->Hidden);
@@ -3912,16 +4072,13 @@ static void ImGui::UpdateKeyboardInputs()
ImGuiContext& g = *GImGui;
ImGuiIO& io = g.IO;
- // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools
- io.KeyMods = GetMergedKeyModFlags();
-
// Import legacy keys or verify they are not used
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
if (io.BackendUsingLegacyKeyArrays == 0)
{
- // Backend used new io.AddKeyEvent() API: Good! Verify that old arrays are never written too.
- for (int n = 0; n < IM_ARRAYSIZE(io.KeysDown); n++)
- IM_ASSERT(io.KeysDown[n] == false && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!");
+ // Backend used new io.AddKeyEvent() API: Good! Verify that old arrays are never written to externally.
+ for (int n = 0; n < ImGuiKey_LegacyNativeKey_END; n++)
+ IM_ASSERT((io.KeysDown[n] == false || IsKeyDown(n)) && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!");
}
else
{
@@ -3944,6 +4101,8 @@ static void ImGui::UpdateKeyboardInputs()
const ImGuiKey key = (ImGuiKey)(io.KeyMap[n] != -1 ? io.KeyMap[n] : n);
IM_ASSERT(io.KeyMap[n] == -1 || IsNamedKey(key));
io.KeysData[key].Down = io.KeysDown[n];
+ if (key != n)
+ io.KeysDown[key] = io.KeysDown[n]; // Allow legacy code using io.KeysDown[GetKeyIndex()] with old backends
io.BackendUsingLegacyKeyArrays = 1;
}
if (io.BackendUsingLegacyKeyArrays == 1)
@@ -3956,6 +4115,9 @@ static void ImGui::UpdateKeyboardInputs()
}
#endif
+ // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools
+ io.KeyMods = GetMergedModFlags();
+
// Clear gamepad data if disabled
if ((io.BackendFlags & ImGuiBackendFlags_HasGamepad) == 0)
for (int i = ImGuiKey_Gamepad_BEGIN; i < ImGuiKey_Gamepad_END; i++)
@@ -4016,13 +4178,16 @@ static void ImGui::UpdateMouseInputs()
io.MouseClickedTime[i] = g.Time;
io.MouseClickedPos[i] = io.MousePos;
io.MouseClickedCount[i] = io.MouseClickedLastCount[i];
+ io.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
io.MouseDragMaxDistanceSqr[i] = 0.0f;
}
else if (io.MouseDown[i])
{
// Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
- float delta_sqr_click_pos = IsMousePosValid(&io.MousePos) ? ImLengthSqr(io.MousePos - io.MouseClickedPos[i]) : 0.0f;
- io.MouseDragMaxDistanceSqr[i] = ImMax(io.MouseDragMaxDistanceSqr[i], delta_sqr_click_pos);
+ ImVec2 delta_from_click_pos = IsMousePosValid(&io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
+ io.MouseDragMaxDistanceSqr[i] = ImMax(io.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
+ io.MouseDragMaxDistanceAbs[i].x = ImMax(io.MouseDragMaxDistanceAbs[i].x, delta_from_click_pos.x < 0.0f ? -delta_from_click_pos.x : delta_from_click_pos.x);
+ io.MouseDragMaxDistanceAbs[i].y = ImMax(io.MouseDragMaxDistanceAbs[i].y, delta_from_click_pos.y < 0.0f ? -delta_from_click_pos.y : delta_from_click_pos.y);
}
// We provide io.MouseDoubleClicked[] as a legacy service
@@ -4142,10 +4307,11 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags()
// - 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.
bool clear_hovered_windows = false;
FindHoveredWindow();
+ IM_ASSERT(g.HoveredWindow == NULL || g.HoveredWindow == g.MovingWindow || g.HoveredWindow->Viewport == g.MouseViewport);
// Modal windows prevents mouse from hovering behind them.
ImGuiWindow* modal_window = GetTopMostPopupModal();
- if (modal_window && g.HoveredWindow && !IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, modal_window))
+ if (modal_window && g.HoveredWindow && !IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, modal_window)) // FIXME-MERGE: RootWindowDockTree ?
clear_hovered_windows = true;
// Disabled mouse?
@@ -4206,15 +4372,16 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags()
io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
}
-ImGuiKeyModFlags ImGui::GetMergedKeyModFlags()
+// [Internal] Do not use directly (can read io.KeyMods instead)
+ImGuiModFlags ImGui::GetMergedModFlags()
{
ImGuiContext& g = *GImGui;
- ImGuiKeyModFlags key_mod_flags = ImGuiKeyModFlags_None;
- if (g.IO.KeyCtrl) { key_mod_flags |= ImGuiKeyModFlags_Ctrl; }
- if (g.IO.KeyShift) { key_mod_flags |= ImGuiKeyModFlags_Shift; }
- if (g.IO.KeyAlt) { key_mod_flags |= ImGuiKeyModFlags_Alt; }
- if (g.IO.KeySuper) { key_mod_flags |= ImGuiKeyModFlags_Super; }
- return key_mod_flags;
+ ImGuiModFlags key_mods = ImGuiModFlags_None;
+ if (g.IO.KeyCtrl) { key_mods |= ImGuiModFlags_Ctrl; }
+ if (g.IO.KeyShift) { key_mods |= ImGuiModFlags_Shift; }
+ if (g.IO.KeyAlt) { key_mods |= ImGuiModFlags_Alt; }
+ if (g.IO.KeySuper) { key_mods |= ImGuiModFlags_Super; }
+ return key_mods;
}
void ImGui::NewFrame()
@@ -4231,7 +4398,9 @@ void ImGui::NewFrame()
CallContextHooks(&g, ImGuiContextHookType_NewFramePre);
// Check and assert for various common IO and Configuration mistakes
+ g.ConfigFlagsLastFrame = g.ConfigFlagsCurrFrame;
ErrorCheckNewFrameSanityChecks();
+ g.ConfigFlagsCurrFrame = g.IO.ConfigFlags;
// Load settings on first frame, save settings when modified (after a delay)
UpdateSettings();
@@ -4253,6 +4422,7 @@ void ImGui::NewFrame()
UpdateViewportsNewFrame();
// 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());
IM_ASSERT(g.Font->IsLoaded());
@@ -4276,6 +4446,7 @@ void ImGui::NewFrame()
for (int n = 0; n < g.Viewports.Size; n++)
{
ImGuiViewportP* viewport = g.Viewports[n];
+ viewport->DrawData = NULL;
viewport->DrawDataP.Clear();
}
@@ -4351,6 +4522,10 @@ void ImGui::NewFrame()
// Update mouse input state
UpdateMouseInputs();
+ // Undocking
+ // (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 UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
UpdateHoveredWindowAndCaptureFlags();
@@ -4413,6 +4588,9 @@ void ImGui::NewFrame()
g.ItemFlagsStack.push_back(ImGuiItemFlags_None);
g.GroupStack.resize(0);
+ // Docking
+ DockContextNewFrameUpdateDocking(&g);
+
// [DEBUG] Update debug features
UpdateDebugToolItemPicker();
UpdateDebugToolStackQueries();
@@ -4428,9 +4606,9 @@ void ImGui::NewFrame()
CallContextHooks(&g, ImGuiContextHookType_NewFramePost);
}
-void ImGui::Initialize(ImGuiContext* context)
+void ImGui::Initialize()
{
- ImGuiContext& g = *context;
+ ImGuiContext& g = *GImGui;
IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
// Add .ini handle for ImGuiWindow type
@@ -4443,27 +4621,34 @@ void ImGui::Initialize(ImGuiContext* context)
ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;
ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll;
ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;
- g.SettingsHandlers.push_back(ini_handler);
+ AddSettingsHandler(&ini_handler);
}
// Add .ini handle for ImGuiTable type
- TableSettingsInstallHandler(context);
+ TableSettingsAddSettingsHandler();
// Create default viewport
ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)();
+ 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]);
#ifdef IMGUI_HAS_DOCK
+ // Initialize Docking
+ DockContextInitialize(&g);
#endif
g.Initialized = true;
}
// This function is merely here to free heap allocations.
-void ImGui::Shutdown(ImGuiContext* context)
+void ImGui::Shutdown()
{
// 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;
+ ImGuiContext& g = *GImGui;
if (g.IO.Fonts && g.FontAtlasOwnedByContext)
{
g.IO.Fonts->Locked = false;
@@ -4477,12 +4662,13 @@ void ImGui::Shutdown(ImGuiContext* context)
// Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
if (g.SettingsLoaded && g.IO.IniFilename != NULL)
- {
- ImGuiContext* backup_context = GImGui;
- SetCurrentContext(&g);
SaveIniSettingsToDisk(g.IO.IniFilename);
- SetCurrentContext(backup_context);
- }
+
+ // Destroy platform windows
+ DestroyPlatformWindows();
+
+ // Shutdown extensions
+ DockContextShutdown(&g);
CallContextHooks(&g, ImGuiContextHookType_Shutdown);
@@ -4503,6 +4689,7 @@ void ImGui::Shutdown(ImGuiContext* context)
g.OpenPopupStack.clear();
g.BeginPopupStack.clear();
+ g.CurrentViewport = g.MouseViewport = g.MouseLastHoveredViewport = NULL;
g.Viewports.clear_delete();
g.TabBars.Clear();
@@ -4601,8 +4788,10 @@ static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* d
static void AddWindowToDrawData(ImGuiWindow* window, int layer)
{
ImGuiContext& g = *GImGui;
- ImGuiViewportP* viewport = g.Viewports[0];
+ 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++)
{
@@ -4643,15 +4832,24 @@ void ImDrawDataBuilder::FlattenIntoSingleLayer()
static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVector<ImDrawList*>* draw_lists)
{
+ // When minimized, we report draw_data->DisplaySize as zero to be consistent with non-viewport mode,
+ // and to allow applications/backends 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;
+
ImGuiIO& io = ImGui::GetIO();
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;
- draw_data->FramebufferScale = io.DisplayFramebufferScale;
+ draw_data->DisplaySize = is_minimized ? ImVec2(0.0f, 0.0f) : viewport->Size;
+ 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++)
{
ImDrawList* draw_list = draw_lists->Data[n];
@@ -4681,12 +4879,20 @@ void ImGui::PopClipRect()
window->ClipRect = window->DrawList->_ClipRectStack.back();
}
+static ImGuiWindow* FindFrontMostVisibleChildWindow(ImGuiWindow* window)
+{
+ for (int n = window->DC.ChildWindows.Size - 1; n >= 0; n--)
+ if (IsWindowActiveAndVisible(window->DC.ChildWindows[n]))
+ return FindFrontMostVisibleChildWindow(window->DC.ChildWindows[n]);
+ return window;
+}
+
static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col)
{
if ((col & IM_COL32_A_MASK) == 0)
return;
- ImGuiViewportP* viewport = (ImGuiViewportP*)GetMainViewport();
+ ImGuiViewportP* viewport = window->Viewport;
ImRect viewport_rect = viewport->GetMainRect();
// Draw behind window by moving the draw command at the FRONT of the draw list
@@ -4694,7 +4900,7 @@ static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32
// 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.
// FIXME: This is creating complication, might be simpler if we could inject a drawlist in drawdata at a given position and not attempt to manipulate ImDrawCmd order.
- ImDrawList* draw_list = window->RootWindow->DrawList;
+ 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
@@ -4706,6 +4912,17 @@ static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32
draw_list->PopClipRect();
draw_list->AddDrawCmd(); // We need to create a command as CmdBuffer.back().IdxOffset won't be correct if we append to same command.
}
+
+ // Draw over sibling docking nodes in a same docking tree
+ 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();
+ }
}
ImGuiWindow* ImGui::FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* parent_window)
@@ -4736,20 +4953,26 @@ static void ImGui::RenderDimmedBackgrounds()
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 or a begin stack child, whichever comes first in draw order.
ImGuiWindow* dim_behind_window = FindBottomMostVisibleWindowWithinBeginStack(modal_window);
RenderDimmedBackgroundBehindWindow(dim_behind_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
+ // 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.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 border around CTRL+Tab target window
ImGuiWindow* window = g.NavWindowingTargetAnim;
- ImGuiViewport* viewport = GetMainViewport();
+ ImGuiViewport* viewport = window->Viewport;
float distance = g.FontSize;
ImRect bb = window->Rect();
bb.Expand(distance);
@@ -4761,6 +4984,19 @@ static void ImGui::RenderDimmedBackgrounds()
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);
+ }
}
// 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.
@@ -4780,7 +5016,10 @@ void ImGui::EndFrame()
// Notify Platform/OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
if (g.IO.SetPlatformImeDataFn && memcmp(&g.PlatformImeData, &g.PlatformImeDataPrev, sizeof(ImGuiPlatformImeData)) != 0)
- g.IO.SetPlatformImeDataFn(GetMainViewport(), &g.PlatformImeData);
+ {
+ ImGuiViewport* viewport = FindViewportByID(g.PlatformImeViewport);
+ g.IO.SetPlatformImeDataFn(viewport ? viewport : GetMainViewport(), &g.PlatformImeData);
+ }
// Hide implicit/fallback "Debug" window if it hasn't been used
g.WithinFrameScopeWithImplicitWindow = false;
@@ -4791,6 +5030,11 @@ 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)
if (g.DragDropActive)
{
@@ -4815,6 +5059,9 @@ void ImGui::EndFrame()
// Initiate moving window + handle left-click and right-click focus
UpdateMouseMovingWindowEndFrame();
+ // 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 children may not exist yet
g.WindowsTempSortBuffer.resize(0);
@@ -4838,7 +5085,6 @@ void ImGui::EndFrame()
// Clear Input data for next frame
g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
g.IO.InputQueueCharacters.resize(0);
- g.IO.KeyModsPrev = g.IO.KeyMods; // doing it here is better than in NewFrame() as we'll tolerate backend writing to KeyMods. If we want to firmly disallow it we should detect it.
memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
CallContextHooks(&g, ImGuiContextHookType_EndFramePost);
@@ -4869,13 +5115,9 @@ 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->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++)
{
@@ -4888,6 +5130,14 @@ 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();
+
+ // 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;
for (int n = 0; n < g.Viewports.Size; n++)
@@ -4895,16 +5145,12 @@ void ImGui::Render()
ImGuiViewportP* viewport = g.Viewports[n];
viewport->DrawDataBuilder.FlattenIntoSingleLayer();
- // Draw software mouse cursor if requested by io.MouseDrawCursor flag
- if (g.IO.MouseDrawCursor && first_render_of_frame)
- RenderMouseCursor(GetForegroundDrawList(viewport), g.IO.MousePos, g.Style.MouseCursorScale, 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));
SetupViewportDrawData(viewport, &viewport->DrawDataBuilder.Layers[0]);
- ImDrawData* draw_data = &viewport->DrawDataP;
+ ImDrawData* draw_data = viewport->DrawData;
g.IO.MetricsRenderVertices += draw_data->TotalVtxCount;
g.IO.MetricsRenderIndices += draw_data->TotalIdxCount;
}
@@ -4948,6 +5194,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.MouseViewport;
+
ImGuiWindow* hovered_window = NULL;
ImGuiWindow* hovered_window_ignoring_moving_window = NULL;
if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
@@ -4963,6 +5214,9 @@ static void FindHoveredWindow()
continue;
if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
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->OuterRectClipped);
@@ -4986,7 +5240,7 @@ static void FindHoveredWindow()
if (hovered_window == NULL)
hovered_window = window;
IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
- 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;
@@ -4994,6 +5248,9 @@ static void FindHoveredWindow()
g.HoveredWindow = hovered_window;
g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window;
+
+ if (g.MovingWindow)
+ g.MovingWindow->Viewport = moving_window_viewport;
}
bool ImGui::IsItemActive()
@@ -5033,6 +5290,13 @@ bool ImGui::IsItemFocused()
ImGuiContext& g = *GImGui;
if (g.NavId != g.LastItemData.ID || g.NavId == 0)
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.
+ ImGuiWindow* window = g.CurrentWindow;
+ if (g.LastItemData.ID == window->ID && window->WriteAccessed)
+ return false;
+
return true;
}
@@ -5140,7 +5404,7 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, b
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
@@ -5268,6 +5532,7 @@ 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)
@@ -5284,10 +5549,19 @@ ImGuiWindow* ImGui::FindWindowByName(const char* name)
static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
{
- window->Pos = ImFloor(ImVec2(settings->Pos.x, settings->Pos.y));
+ const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
+ window->ViewportPos = main_viewport->Pos;
+ if (settings->ViewportId)
+ {
+ window->ViewportId = settings->ViewportId;
+ window->ViewportPos = ImVec2(settings->ViewportPos.x, settings->ViewportPos.y);
+ }
+ window->Pos = ImFloor(ImVec2(settings->Pos.x + window->ViewportPos.x, settings->Pos.y + window->ViewportPos.y));
if (settings->Size.x > 0 && settings->Size.y > 0)
window->Size = window->SizeFull = ImFloor(ImVec2(settings->Size.x, settings->Size.y));
window->Collapsed = settings->Collapsed;
+ window->DockId = settings->DockId;
+ window->DockOrder = settings->DockOrder;
}
static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags)
@@ -5326,6 +5600,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.
const 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))
@@ -5336,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)
{
@@ -5361,6 +5636,16 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)
return window;
}
+static ImGuiWindow* GetWindowForTitleDisplay(ImGuiWindow* window)
+{
+ return window->DockNodeAsHost ? window->DockNodeAsHost->VisibleWindow : window;
+}
+
+static ImGuiWindow* GetWindowForTitleAndMenuHeight(ImGuiWindow* window)
+{
+ return (window->DockNodeAsHost && window->DockNodeAsHost->VisibleWindow) ? window->DockNodeAsHost->VisibleWindow : window;
+}
+
static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& size_desired)
{
ImGuiContext& g = *GImGui;
@@ -5388,7 +5673,7 @@ static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& s
// Minimum size
if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
{
- ImGuiWindow* window_for_height = window;
+ ImGuiWindow* window_for_height = GetWindowForTitleAndMenuHeight(window);
const float decoration_up_height = window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight();
new_size = ImMax(new_size, g.Style.WindowMinSize);
new_size.y = ImMax(new_size.y, decoration_up_height + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows
@@ -5438,7 +5723,12 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont
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 = ImGui::GetMainViewport()->Size;
+ ImVec2 avail_size = window->Viewport->Size;
+ if (window->ViewportOwned)
+ 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].WorkSize;
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),
@@ -5468,7 +5758,7 @@ static ImGuiCol GetWindowBgColorIdx(ImGuiWindow* window)
{
if (window->Flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
return ImGuiCol_PopupBg;
- if (window->Flags & ImGuiWindowFlags_ChildWindow)
+ if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !window->DockIsActive)
return ImGuiCol_ChildBg;
return ImGuiCol_WindowBg;
}
@@ -5534,7 +5824,7 @@ static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_
ImGuiID ImGui::GetWindowResizeCornerID(ImGuiWindow* window, int n)
{
IM_ASSERT(n >= 0 && n < 4);
- ImGuiID id = window->ID;
+ ImGuiID id = window->DockIsActive ? window->DockNode->HostWindow->ID : window->ID;
id = ImHashStr("#RESIZE", 0, id);
id = ImHashData(&n, sizeof(int), id);
return id;
@@ -5545,7 +5835,7 @@ ImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir)
{
IM_ASSERT(dir >= 0 && dir < 4);
int n = (int)dir + 4;
- ImGuiID id = window->ID;
+ ImGuiID id = window->DockIsActive ? window->DockNode->HostWindow->ID : window->ID;
id = ImHashStr("#RESIZE", 0, id);
id = ImHashData(&n, sizeof(int), id);
return id;
@@ -5572,6 +5862,16 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s
ImVec2 pos_target(FLT_MAX, FLT_MAX);
ImVec2 size_target(FLT_MAX, FLT_MAX);
+ // 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 backends 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)
+ window->ClipRect = window->Viewport->GetMainRect();
+
// Resize grips and borders are on layer 1
window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
@@ -5588,6 +5888,7 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s
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);
ImGuiID resize_grip_id = window->GetID(resize_grip_n); // == GetWindowResizeCornerID()
+ KeepAliveID(resize_grip_id);
ButtonBehavior(resize_rect, resize_grip_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
//GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
if (hovered || held)
@@ -5623,6 +5924,7 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s
bool hovered, held;
ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_HOVER_PADDING);
ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID()
+ KeepAliveID(border_id);
ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
//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)
@@ -5647,7 +5949,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_Keyboard && g.IO.KeyShift)
@@ -5687,8 +5989,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);
}
@@ -5709,7 +6011,7 @@ static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f);
window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), 0, ImMax(2.0f, border_size)); // Thicker than usual
}
- if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
+ 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);
@@ -5718,7 +6020,7 @@ static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
// Draw background and borders
// Draw and handle scrollbars
-void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size)
+void ImGui::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)
{
ImGuiContext& g = *GImGui;
ImGuiStyle& style = g.Style;
@@ -5746,21 +6048,57 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar
// 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(GetWindowBgColorIdx(window));
- bool override_alpha = false;
- float alpha = 1.0f;
- if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha)
+ if (window->ViewportOwned)
{
- alpha = g.NextWindowData.BgAlphaVal;
- override_alpha = true;
+ // No alpha
+ bg_col = (bg_col | IM_COL32_A_MASK);
+ if (is_docking_transparent_payload)
+ window->Viewport->Alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA;
}
- 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);
+ else
+ {
+ // Adjust alpha. For docking
+ bool override_alpha = false;
+ float alpha = 1.0f;
+ if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha)
+ {
+ alpha = g.NextWindowData.BgAlphaVal;
+ override_alpha = true;
+ }
+ if (is_docking_transparent_payload)
+ {
+ alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA; // FIXME-DOCK: Should that be an override?
+ override_alpha = true;
+ }
+ if (override_alpha)
+ bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
+ }
+
+ // 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
- 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(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_RoundCornersTop);
@@ -5776,6 +6114,25 @@ 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), drag from small triangle to quickly undock
+ ImGuiDockNode* node = window->DockNode;
+ 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);
+ 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))
+ node->WantHiddenTabBarToggle = true;
+ else if (held && IsMouseDragging(0))
+ 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);
+ window->DrawList->AddTriangleFilled(p, p + ImVec2(unhide_sz_draw, 0.0f), p + ImVec2(0.0f, unhide_sz_draw), col);
+ }
+
// Scrollbars
if (window->ScrollbarX)
Scrollbar(ImGuiAxis_X);
@@ -5783,7 +6140,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar
Scrollbar(ImGuiAxis_Y);
// 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++)
{
@@ -5796,12 +6153,14 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar
}
}
- // Borders
- RenderWindowOuterBorders(window);
+ // Borders (for dock node host they will be rendered over after the tab bar)
+ if (handle_borders_and_resize_grips && !window->DockNodeAsHost)
+ RenderWindowOuterBorders(window);
}
}
// Render title text, collapse button, close button
+// 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;
@@ -5842,7 +6201,7 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl
// Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
if (has_collapse_button)
- if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos))
+ if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos, NULL))
window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function
// Close button
@@ -5893,9 +6252,13 @@ 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->RootWindowPopupTree = 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->RootWindow = parent_window->RootWindow;
+ {
+ 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)))
@@ -5973,21 +6336,24 @@ 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];
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;
window->LastTimeActive = (float)g.Time;
@@ -5999,8 +6365,42 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
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
+ if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasDock)
+ SetWindowDock(window, g.NextWindowData.DockId, g.NextWindowData.DockCond);
+ if (first_begin_of_the_frame)
+ {
+ 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);
+ flags = window->Flags;
+ if (window->DockIsActive)
+ {
+ IM_ASSERT(window->DockNode != NULL);
+ 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)
+ {
+ window->Appearing = true;
+ SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
+ }
+ }
+ else
+ {
+ window->DockIsActive = window->DockNodeIsVisible = window->DockTabIsVisible = false;
+ }
+ }
+
// 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().Window;
+ ImGuiWindow* parent_window_in_stack = window->DockIsActive ? 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));
@@ -6078,6 +6478,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
else if (first_begin_of_the_frame)
window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);
+ if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasWindowClass)
+ window->WindowClass = g.NextWindowData.WindowClass;
if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed)
SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus)
@@ -6097,6 +6499,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)
@@ -6105,7 +6512,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
// The title bar always display the 'name' parameter, so we only update the string storage if it needs to be visible to the end-user elsewhere.
bool window_title_visible_elsewhere = false;
- if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB
+ if ((window->Viewport && window->Viewport->Window == window) || (window->DockIsActive))
+ window_title_visible_elsewhere = true;
+ else if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB
window_title_visible_elsewhere = true;
if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
{
@@ -6118,6 +6527,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)
@@ -6145,18 +6558,25 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
}
// SELECT VIEWPORT
- // FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style)
+ // We need to do this before using any style/font sizes, as viewport with a different DPI may affect font sizes.
+
+ WindowSelectViewport(window);
+ SetCurrentViewport(window, window->Viewport);
+ window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f;
SetCurrentWindow(window);
+ 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
window->WindowBorderSize = ((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 && (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;
// 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);
@@ -6164,7 +6584,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// 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();
@@ -6257,24 +6677,57 @@ 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->Flags & ImGuiViewportFlags_Minimized))
+ 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;
+ 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
+ SetCurrentViewport(window, window->Viewport);
+ window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f;
+ SetCurrentWindow(window);
+ }
+
+ if (window->ViewportOwned)
+ 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.
- ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport();
- ImRect viewport_rect(viewport->GetMainRect());
- ImRect viewport_work_rect(viewport->GetWorkRect());
+ 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()
if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
- if (viewport_rect.GetWidth() > 0.0f && viewport_rect.GetHeight() > 0.0f)
+ {
+ if (!window->ViewportOwned && viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f)
+ {
ClampWindowRect(window, visibility_rect);
+ }
+ else if (window->ViewportOwned && g.PlatformIO.Monitors.Size > 0)
+ {
+ // 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);
// Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
// Large values tend to lead to variety of artifacts and are not recommended.
- window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
+ 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;
// For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts.
//if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar))
@@ -6286,7 +6739,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
{
if (flags & ImGuiWindowFlags_Popup)
want_focus = true;
- else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0)
+ else if ((window->DockIsActive || (flags & ImGuiWindowFlags_ChildWindow) == 0) && !(flags & ImGuiWindowFlags_Tooltip))
want_focus = true;
ImGuiWindow* modal = GetTopMostPopupModal();
@@ -6318,16 +6771,33 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
}
#endif
+ // 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] = {};
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.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
- if (!window->Collapsed)
+ if (handle_borders_and_resize_grips && !window->Collapsed)
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;
+ // Synchronize window --> viewport again and one last time (clamping and manual resize may have affected either)
+ if (window->ViewportOwned)
+ {
+ if (!window->Viewport->PlatformRequestMove)
+ window->Viewport->Pos = window->Pos;
+ if (!window->Viewport->PlatformRequestResize)
+ window->Viewport->Size = window->Size;
+ window->Viewport->UpdateWorkRect();
+ viewport_rect = window->Viewport->GetMainRect();
+ }
+
+ // 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).
@@ -6361,6 +6831,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
const ImRect outer_rect = window->Rect();
const ImRect title_bar_rect = window->TitleBarRect();
window->OuterRectClipped = outer_rect;
+ if (window->DockIsActive)
+ window->OuterRectClipped.Min.y += window->TitleBarHeight();
window->OuterRectClipped.ClipWith(host_rect);
// Inner rectangle
@@ -6416,6 +6888,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// 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)
+ const bool is_undocked_or_docked_visible = !window->DockIsActive || window->DockTabIsVisible;
+ if (is_undocked_or_docked_visible)
{
bool render_decorations_in_parent = false;
if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
@@ -6433,8 +6907,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// Handle title bar, scrollbar, resize grips and resize borders
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);
- RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, resize_grip_count, resize_grip_col, resize_grip_draw_size);
+ 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;
@@ -6484,10 +6958,10 @@ 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.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);
@@ -6518,8 +6992,20 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
NavInitWindow(window, false); // <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls
}
+ // Close requested by platform window
+ if (p_open != NULL && window->Viewport->PlatformRequestClose && window->Viewport != GetMainViewport())
+ {
+ 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.
+ IMGUI_DEBUG_LOG_VIEWPORT("Window '%s' PlatformRequestClose\n", window->Name);
+ *p_open = false;
+ }
+ }
+
// Title bar
- if (!(flags & ImGuiWindowFlags_NoTitleBar))
+ if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
RenderWindowTitleBarContents(window, ImRect(title_bar_rect.Min.x + window->WindowBorderSize, title_bar_rect.Min.y, title_bar_rect.Max.x - window->WindowBorderSize, title_bar_rect.Max.y), name, p_open);
// Clear hit test shape every frame
@@ -6535,9 +7021,27 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
LogToClipboard();
*/
+ 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 BeginDockableDragDropSource() also overwrites it.
+ if ((g.MovingWindow == window) && (g.IO.ConfigDockingWithShift == g.IO.KeyShift))
+ 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->RootWindowDockTree != window)
+ if ((window == window->RootWindowDockTree) && !(window->Flags & ImGuiWindowFlags_DockNodeHost))
+ BeginDockableDragDropTarget(window);
+ }
+
// We fill last item data based on Title Bar/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.
- SetLastItemData(window->MoveId, g.CurrentItemFlags, IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, title_bar_rect);
+ if (window->DockIsActive)
+ SetLastItemData(window->MoveId, g.CurrentItemFlags, window->DockTabItemStatusFlags, window->DockTabItemRect);
+ else
+ SetLastItemData(window->MoveId, g.CurrentItemFlags, IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, title_bar_rect);
// [Test Engine] Register title bar / tab
if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
@@ -6546,13 +7050,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
else
{
// Append
+ SetCurrentViewport(window, window->Viewport);
SetCurrentWindow(window);
}
// Pull/inherit current state
window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : window->GetID("#FOCUSSCOPE"); // Inherit from parent only // -V595
- 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)
window->WriteAccessed = false;
@@ -6562,11 +7068,23 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// Update visibility
if (first_begin_of_the_frame)
{
+ // 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);
+ IM_ASSERT((flags& ImGuiWindowFlags_NoTitleBar) != 0 || (window->DockIsActive));
if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) // FIXME: Doesn't make sense for ChildWindow??
{
const bool nav_request = (flags & ImGuiWindowFlags_NavFlattened) && (g.NavAnyRequest && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);
@@ -6603,6 +7121,16 @@ 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;
+
+ // 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.
+ 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;
@@ -6622,18 +7150,24 @@ 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)
+ 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
if (window->DC.CurrentColumns)
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-DOCK
+ host_window->DC.CursorMaxPos = window->DC.CursorMaxPos + window->WindowPadding - host_window->WindowPadding;
+
// Pop from window stack
g.LastItemData = g.CurrentWindowStack.back().ParentLastItemDataBackup;
if (window->Flags & ImGuiWindowFlags_ChildMenu)
@@ -6643,6 +7177,8 @@ void ImGui::End()
g.CurrentWindowStack.back().StackSizesOnBegin.CompareWithCurrentState();
g.CurrentWindowStack.pop_back();
SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window);
+ if (g.CurrentWindow)
+ SetCurrentViewport(g.CurrentWindow, g.CurrentWindow->Viewport);
}
void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
@@ -6670,7 +7206,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)
@@ -6746,24 +7282,32 @@ 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->RootWindow : NULL; // NB: In docking branch this is window->RootWindowDockStop
- 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);
// 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->RootWindow != focus_front_window)
- if (!g.ActiveIdNoClearOnFocusLoss)
+ if (!g.ActiveIdNoClearOnFocusLoss && !active_id_window_is_dock_node_host)
ClearActiveID();
// Passing NULL allow to disable keyboard focus
if (!window)
return;
+ window->LastFrameJustFocused = g.FrameCount;
+
+ // Select in dock node
+ if (dock_node && dock_node->TabBar)
+ dock_node->TabBar->SelectedTabId = dock_node->TabBar->NextSelectedTabId = window->TabId;
// 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);
}
@@ -6790,6 +7334,10 @@ void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWind
if (window != ignore_window && window->WasActive)
if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
{
+ // 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?
ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window);
FocusWindow(focus_window);
return;
@@ -6923,7 +7471,7 @@ void ImGui::PopTextWrapPos()
window->DC.TextWrapPosStack.pop_back();
}
-static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy)
+static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy, bool dock_hierarchy)
{
ImGuiWindow* last_window = NULL;
while (last_window != window)
@@ -6932,13 +7480,15 @@ static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierar
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 ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy, bool dock_hierarchy)
{
- ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy);
+ ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy, dock_hierarchy);
if (window_root == potential_parent)
return true;
while (window != NULL)
@@ -6998,12 +7548,13 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
{
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 = GetCombinedRootWindow(cur_window, popup_hierarchy);
+ cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy, dock_hierarchy);
bool result;
if (flags & ImGuiHoveredFlags_ChildWindows)
- result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy);
+ result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy, dock_hierarchy);
else
result = (ref_window == cur_window);
if (!result)
@@ -7031,15 +7582,28 @@ bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
IM_ASSERT(cur_window); // Not inside a Begin()/End()
const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0;
+ const bool dock_hierarchy = (flags & ImGuiFocusedFlags_DockHierarchy) != 0;
if (flags & ImGuiHoveredFlags_RootWindow)
- cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy);
+ cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy, dock_hierarchy);
if (flags & ImGuiHoveredFlags_ChildWindows)
- return IsWindowChildOf(ref_window, cur_window, popup_hierarchy);
+ return IsWindowChildOf(ref_window, cur_window, popup_hierarchy, dock_hierarchy);
else
return (ref_window == cur_window);
}
+ImGuiID ImGui::GetWindowDockID()
+{
+ ImGuiContext& g = *GImGui;
+ return g.CurrentWindow->DockId;
+}
+
+bool ImGui::IsWindowDocked()
+{
+ ImGuiContext& g = *GImGui;
+ return g.CurrentWindow->DockIsActive;
+}
+
// Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
// Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmatically.
// If you want a window to never be focused, you may use the e.g. NoInputs flag.
@@ -7081,6 +7645,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;
@@ -7215,6 +7780,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)
@@ -7273,12 +7839,48 @@ void ImGui::SetNextWindowBgAlpha(float alpha)
g.NextWindowData.BgAlphaVal = alpha;
}
+void ImGui::SetNextWindowViewport(ImGuiID id)
+{
+ ImGuiContext& g = *GImGui;
+ g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasViewport;
+ g.NextWindowData.ViewportId = id;
+}
+
+void ImGui::SetNextWindowDockID(ImGuiID id, ImGuiCond cond)
+{
+ ImGuiContext& g = *GImGui;
+ g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasDock;
+ g.NextWindowData.DockCond = cond ? cond : ImGuiCond_Always;
+ g.NextWindowData.DockId = id;
+}
+
+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;
+}
+
ImDrawList* ImGui::GetWindowDrawList()
{
ImGuiWindow* window = GetCurrentWindow();
return window->DrawList;
}
+float ImGui::GetWindowDpiScale()
+{
+ ImGuiContext& g = *GImGui;
+ return g.CurrentDpiScale;
+}
+
+ImGuiViewport* ImGui::GetWindowViewport()
+{
+ ImGuiContext& g = *GImGui;
+ IM_ASSERT(g.CurrentViewport != NULL && g.CurrentViewport == g.CurrentWindow->Viewport);
+ return g.CurrentViewport;
+}
+
ImFont* ImGui::GetFont()
{
return GImGui->Font;
@@ -7381,7 +7983,7 @@ void ImGui::PushID(const char* str_id)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
- ImGuiID id = window->GetIDNoKeepAlive(str_id);
+ ImGuiID id = window->GetID(str_id);
window->IDStack.push_back(id);
}
@@ -7389,7 +7991,7 @@ void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
- ImGuiID id = window->GetIDNoKeepAlive(str_id_begin, str_id_end);
+ ImGuiID id = window->GetID(str_id_begin, str_id_end);
window->IDStack.push_back(id);
}
@@ -7397,7 +7999,7 @@ void ImGui::PushID(const void* ptr_id)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
- ImGuiID id = window->GetIDNoKeepAlive(ptr_id);
+ ImGuiID id = window->GetID(ptr_id);
window->IDStack.push_back(id);
}
@@ -7405,7 +8007,7 @@ void ImGui::PushID(int int_id)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
- ImGuiID id = window->GetIDNoKeepAlive(int_id);
+ ImGuiID id = window->GetID(int_id);
window->IDStack.push_back(id);
}
@@ -7490,6 +8092,8 @@ 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->GetMainRect().Overlaps(rect_clipped))
+ return false;
return true;
}
@@ -7631,14 +8235,8 @@ bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)
const float t = g.IO.MouseDownDuration[button];
if (t == 0.0f)
return true;
-
if (repeat && t > g.IO.KeyRepeatDelay)
- {
- // FIXME: 2019/05/03: Our old repeat code was wrong here and led to doubling the repeat rate, which made it an ok rate for repeat on mouse hold.
- int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f);
- if (amount > 0)
- return true;
- }
+ return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
return false;
}
@@ -7774,8 +8372,18 @@ static const char* GetInputSourceName(ImGuiInputSource source)
return input_source_names[source];
}
+/*static void DebugLogInputEvent(const char* prefix, const ImGuiInputEvent* e)
+{
+ if (e->Type == ImGuiInputEventType_MousePos) { IMGUI_DEBUG_LOG("%s: MousePos (%.1f %.1f)\n", prefix, e->MousePos.PosX, e->MousePos.PosY); return; }
+ if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG("%s: MouseButton %d %s\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up"); return; }
+ if (e->Type == ImGuiInputEventType_MouseWheel) { IMGUI_DEBUG_LOG("%s: MouseWheel (%.1f %.1f)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY); return; }
+ if (e->Type == ImGuiInputEventType_Key) { IMGUI_DEBUG_LOG("%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("%s: Text: %c (U+%08X)\n", prefix, e->Text.Char, e->Text.Char); return; }
+ if (e->Type == ImGuiInputEventType_Focus) { IMGUI_DEBUG_LOG("%s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; }
+}*/
// Process input queue
+// We always call this with the value of 'bool g.IO.ConfigInputTrickleEventQueue'.
// - trickle_fast_inputs = false : process all events, turn into flattened input state (e.g. successive down/up/down/up will be lost)
// - trickle_fast_inputs = true : process as many events as possible (successive down/up/down/up will be trickled over several frames so nothing is lost) (new feature in 1.87)
void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
@@ -7783,7 +8391,12 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
ImGuiContext& g = *GImGui;
ImGuiIO& io = g.IO;
- bool mouse_moved = false, mouse_wheeled = false, key_changed = false, text_inputed = false;
+ // Only trickle chars<>key when working with InputText()
+ // FIXME: InputText() could parse event trail?
+ // FIXME: Could specialize chars<>keys trickling rules for control keys (those not typically associated to characters)
+ const bool trickle_interleaved_keys_and_text = (trickle_fast_inputs && g.WantTextInputNextFrame == 1);
+
+ bool mouse_moved = false, mouse_wheeled = false, key_changed = false, text_inputted = false;
int mouse_button_changed = 0x00;
ImBitArray<ImGuiKey_KeysData_SIZE> key_changed_mask;
@@ -7799,7 +8412,7 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
if (io.MousePos.x != event_pos.x || io.MousePos.y != event_pos.y)
{
// Trickling Rule: Stop processing queued events if we already handled a mouse button change
- if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || text_inputed))
+ if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || text_inputted))
break;
io.MousePos = event_pos;
mouse_moved = true;
@@ -7830,6 +8443,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)
{
ImGuiKey key = e->Key.Key;
@@ -7839,7 +8456,7 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
if (keydata->Down != e->Key.Down || keydata->AnalogValue != e->Key.AnalogValue)
{
// Trickling Rule: Stop processing queued events if we got multiple action on the same button
- if (trickle_fast_inputs && keydata->Down != e->Key.Down && (key_changed_mask.TestBit(keydata_index) || text_inputed || mouse_button_changed != 0))
+ if (trickle_fast_inputs && keydata->Down != e->Key.Down && (key_changed_mask.TestBit(keydata_index) || text_inputted || mouse_button_changed != 0))
break;
keydata->Down = e->Key.Down;
keydata->AnalogValue = e->Key.AnalogValue;
@@ -7852,18 +8469,26 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
if (key == ImGuiKey_ModShift) { io.KeyShift = keydata->Down; }
if (key == ImGuiKey_ModAlt) { io.KeyAlt = keydata->Down; }
if (key == ImGuiKey_ModSuper) { io.KeySuper = keydata->Down; }
- io.KeyMods = GetMergedKeyModFlags();
+ io.KeyMods = GetMergedModFlags();
}
+
+ // Allow legacy code using io.KeysDown[GetKeyIndex()] with new backends
+#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
+ io.KeysDown[key] = keydata->Down;
+ if (io.KeyMap[key] != -1)
+ io.KeysDown[io.KeyMap[key]] = keydata->Down;
+#endif
}
}
- else if (e->Type == ImGuiInputEventType_Char)
+ else if (e->Type == ImGuiInputEventType_Text)
{
// Trickling Rule: Stop processing queued events if keys/mouse have been interacted with
- if (trickle_fast_inputs && (key_changed || mouse_button_changed != 0 || mouse_moved || mouse_wheeled))
+ if (trickle_fast_inputs && ((key_changed && trickle_interleaved_keys_and_text) || mouse_button_changed != 0 || mouse_moved || mouse_wheeled))
break;
unsigned int c = e->Text.Char;
io.InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID);
- text_inputed = true;
+ if (trickle_interleaved_keys_and_text)
+ text_inputted = true;
}
else if (e->Type == ImGuiInputEventType_Focus)
{
@@ -7882,6 +8507,11 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
for (int n = 0; n < event_n; n++)
g.InputEventsTrail.push_back(g.InputEventsQueue[n]);
+ // [DEBUG]
+ /*if (event_n != 0)
+ for (int n = 0; n < g.InputEventsQueue.Size; n++)
+ DebugLogInputEvent(n < event_n ? "Processed" : "Remaining", &g.InputEventsQueue[n]);*/
+
// Remaining events will be processed on the next frame
if (event_n == g.InputEventsQueue.Size)
g.InputEventsQueue.resize(0);
@@ -7904,9 +8534,12 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
// Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui.
// Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
-// If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. your user code
-// may see different structures than what imgui.cpp sees, which is problematic.
-// We usually require settings to be in imconfig.h to make sure that they are accessible to all compilation units involved with Dear ImGui.
+// If this triggers you have an issue:
+// - Most commonly: mismatched headers and compiled code version.
+// - Or: mismatched configuration #define, compilation settings, packing pragma etc.
+// The configuration settings mentioned in imconfig.h must be set for all compilation units involved with Dear ImGui,
+// which is way it is required you put them in your imconfig file (and not just before including imgui.h).
+// Otherwise it is possible that different compilation units would see different structure layout
bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert, size_t sz_idx)
{
bool error = false;
@@ -7940,13 +8573,14 @@ static void ImGui::ErrorCheckNewFrameSanityChecks()
IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!");
IM_ASSERT(g.IO.Fonts->IsBuilt() && "Font Atlas not built! Make sure you called ImGui_ImplXXXX_NewFrame() function for renderer backend, which should call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()");
IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!");
- IM_ASSERT(g.Style.CircleTessellationMaxError > 0.0f && "Invalid style setting!");
+ IM_ASSERT(g.Style.CircleTessellationMaxError > 0.0f && "Invalid style setting!");
IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting!"); // Allows us to avoid a few clamps in color computations
IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting.");
IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
+ IM_ASSERT(g.Style.ColorButtonPosition == ImGuiDir_Left || g.Style.ColorButtonPosition == ImGuiDir_Right);
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
for (int n = ImGuiKey_NamedKey_BEGIN; 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..511, or -1 for unmapped key)");
+ IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < ImGuiKey_LegacyNativeKey_END && "io.KeyMap[] contains an out of bound value (need to be 0..511, or -1 for unmapped key)");
// Check: 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 added in 1.60 WIP)
if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && g.IO.BackendUsingLegacyKeyArrays == 1)
@@ -7956,6 +8590,46 @@ static void ImGui::ErrorCheckNewFrameSanityChecks()
// Check: the io.ConfigWindowsResizeFromEdges option requires backend 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;
+
+ // 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.
+ 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)
+ 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)
+ {
+ 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_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.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))
+ IM_ASSERT(g.PlatformIO.Platform_SetWindowAlpha != NULL && "Platform_SetWindowAlpha handler is required to use io.ConfigDockingTransparent!");
+ }
+ else
+ {
+ // Disable feature, our backends 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];
+ 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);
+ }
+ }
}
static void ImGui::ErrorCheckEndFrameSanityChecks()
@@ -7968,11 +8642,11 @@ static void ImGui::ErrorCheckEndFrameSanityChecks()
// send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs.
// We silently accommodate for this case by ignoring/ the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0),
// while still correctly asserting on mid-frame key press events.
- const ImGuiKeyModFlags key_mod_flags = GetMergedKeyModFlags();
- IM_ASSERT((key_mod_flags == 0 || g.IO.KeyMods == key_mod_flags) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
- IM_UNUSED(key_mod_flags);
+ const ImGuiModFlags key_mods = GetMergedModFlags();
+ IM_ASSERT((key_mods == 0 || g.IO.KeyMods == key_mods) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
+ IM_UNUSED(key_mods);
- // Recover from errors
+ // [EXPERIMENTAL] Recover from errors: You may call this yourself before EndFrame().
//ErrorCheckEndFrameRecover();
// Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you
@@ -8012,7 +8686,6 @@ void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi
IM_ASSERT(window->IsFallbackWindow);
break;
}
- IM_ASSERT(window == g.CurrentWindow);
if (window->Flags & ImGuiWindowFlags_ChildWindow)
{
if (log_callback) log_callback(user_data, "Recovered from missing EndChild() for '%s'", window->Name);
@@ -8155,7 +8828,7 @@ void ImGuiStackSizes::CompareWithCurrentState()
// - GetWindowContentRegionMin(), GetWindowContentRegionMax()
// - BeginGroup()
// - EndGroup()
-// Also see in imgui_widgets: tab bars, columns.
+// Also see in imgui_widgets: tab bars, and in imgui_tables: tables, columns.
//-----------------------------------------------------------------------------
// Advance cursor given item size for layout.
@@ -8172,14 +8845,16 @@ void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
// In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
// but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
- const float line_height = ImMax(window->DC.CurrLineSize.y, size.y + offset_to_match_baseline_y);
+
+ const float line_y1 = window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y;
+ const float line_height = ImMax(window->DC.CurrLineSize.y, /*ImMax(*/window->DC.CursorPos.y - line_y1/*, 0.0f)*/ + size.y + offset_to_match_baseline_y);
// Always align ourselves on pixel boundaries
//if (g.IO.KeyAlt) window->DrawList->AddRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(size.x, line_height), IM_COL32(255,0,0,200)); // [DEBUG]
window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
- window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y;
+ window->DC.CursorPosPrevLine.y = line_y1;
window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line
- window->DC.CursorPos.y = IM_FLOOR(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y); // Next line
+ window->DC.CursorPos.y = IM_FLOOR(line_y1 + line_height + g.Style.ItemSpacing.y); // Next line
window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
//if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
@@ -8188,17 +8863,13 @@ 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;
// Horizontal layout mode
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
SameLine();
}
-void ImGui::ItemSize(const ImRect& bb, float text_baseline_y)
-{
- ItemSize(bb.GetSize(), text_baseline_y);
-}
-
// 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 provide a larger region to ItemAdd() which is used drawing/interaction.
@@ -8218,6 +8889,8 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu
// Directional navigation processing
if (id != 0)
{
+ KeepAliveID(id);
+
// Runs prior to clipping early-out
// (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
// (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
@@ -8273,25 +8946,28 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu
// spacing_w >= 0 : enforce spacing amount
void ImGui::SameLine(float offset_from_start_x, float spacing_w)
{
- ImGuiWindow* window = GetCurrentWindow();
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
if (window->SkipItems)
return;
- ImGuiContext& g = *GImGui;
if (offset_from_start_x != 0.0f)
{
- if (spacing_w < 0.0f) spacing_w = 0.0f;
+ if (spacing_w < 0.0f)
+ spacing_w = 0.0f;
window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
}
else
{
- if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
+ if (spacing_w < 0.0f)
+ spacing_w = g.Style.ItemSpacing.x;
window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
}
window->DC.CurrLineSize = window->DC.PrevLineSize;
window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
+ window->DC.IsSameLine = true;
}
ImVec2 ImGui::GetCursorScreenPos()
@@ -8436,7 +9112,8 @@ float ImGui::CalcItemWidth()
// The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable)
ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)
{
- ImGuiWindow* window = GImGui->CurrentWindow;
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
ImVec2 region_max;
if (size.x < 0.0f || size.y < 0.0f)
@@ -8900,7 +9577,7 @@ void ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags ext
window->HiddenFramesCanSkipItems = 1; // FIXME: This may not be necessary?
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 flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking;
Begin(window_name, NULL, flags | extra_window_flags);
}
@@ -9077,11 +9754,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->RootWindowDockTree == ref_window->RootWindowDockTree) // FIXME-MERGE
if (IsWindowWithinBeginStackOf(ref_window, popup_window))
{
ref_window_is_descendent_of_popup = true;
@@ -9187,7 +9865,7 @@ bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags)
else
ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
- 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();
@@ -9226,11 +9904,11 @@ 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)
{
- const ImGuiViewport* viewport = GetMainViewport();
+ 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;
+ 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)
{
@@ -9417,8 +10095,19 @@ ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& s
ImRect ImGui::GetPopupAllowedExtentRect(ImGuiWindow* window)
{
ImGuiContext& g = *GImGui;
- IM_UNUSED(window);
- ImRect r_screen = ((ImGuiViewportP*)(void*)GetMainViewport())->GetMainRect();
+ 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
+ {
+ // Use the full viewport area (not work area) for popups
+ 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));
return r_screen;
@@ -9433,8 +10122,7 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
{
// 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.
- IM_ASSERT(g.CurrentWindow == window);
- ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2].Window;
+ 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_avoid;
if (parent_window->DC.MenuBarAppending)
@@ -9691,10 +10379,10 @@ static void ImGui::NavProcessItem()
const ImGuiItemFlags item_flags = g.LastItemData.InFlags;
// Process Init Request
- if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
+ if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent && (item_flags & ImGuiItemFlags_Disabled) == 0)
{
// Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
- const bool candidate_for_nav_default_focus = (item_flags & (ImGuiItemFlags_NoNavDefaultFocus | ImGuiItemFlags_Disabled)) == 0;
+ const bool candidate_for_nav_default_focus = (item_flags & ImGuiItemFlags_NoNavDefaultFocus) == 0;
if (candidate_for_nav_default_focus || g.NavInitResultId == 0)
{
g.NavInitResultId = id;
@@ -9880,6 +10568,9 @@ static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* 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;
}
@@ -9918,6 +10609,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);
@@ -9970,7 +10662,7 @@ static ImVec2 ImGui::NavCalcPreferredRefPos()
rect_rel.Translate(window->Scroll - next_scroll);
}
ImVec2 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()));
- ImGuiViewport* viewport = GetMainViewport();
+ ImGuiViewport* viewport = window->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.
}
}
@@ -10770,7 +11462,7 @@ static void ImGui::NavUpdateWindowing()
// - 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.
const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
- if (nav_keyboard_active && io.KeyMods == ImGuiKeyModFlags_Alt && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) == 0)
+ if (nav_keyboard_active && IsKeyPressed(ImGuiKey_ModAlt))
{
g.NavWindowingToggleLayer = true;
g.NavInputSource = ImGuiInputSource_Keyboard;
@@ -10783,13 +11475,12 @@ static void ImGui::NavUpdateWindowing()
g.NavWindowingToggleLayer = false;
// Apply layer toggle on release
- // Important: we don't assume that Alt was previously held in order to handle loss of focus when backend calls io.AddFocusEvent(false)
// Important: as before version <18314 we lacked an explicit IO event for focus gain/loss, we also compare mouse validity to detect old backends clearing mouse pos on focus loss.
- if (!(io.KeyMods & ImGuiKeyModFlags_Alt) && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) && g.NavWindowingToggleLayer)
+ if (IsKeyReleased(ImGuiKey_ModAlt) && g.NavWindowingToggleLayer)
if (g.ActiveId == 0 || g.ActiveIdAllowOverlap)
if (IsMousePosValid(&io.MousePos) == IsMousePosValid(&io.MousePosPrev))
apply_toggle_layer = true;
- if (!io.KeyAlt)
+ if (!IsKeyDown(ImGuiKey_ModAlt))
g.NavWindowingToggleLayer = false;
}
@@ -10805,7 +11496,7 @@ static void ImGui::NavUpdateWindowing()
{
const float NAV_MOVE_SPEED = 800.0f;
const float move_speed = ImFloor(NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, 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;
@@ -10815,6 +11506,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;
ClearActiveID();
NavRestoreHighlightAfterMove();
apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
@@ -10832,6 +11524,10 @@ static void ImGui::NavUpdateWindowing()
// won't be valid.
if (apply_focus_window->DC.NavLayersActiveMaskNext == (1 << ImGuiNavLayer_Menu))
g.NavLayer = ImGuiNavLayer_Menu;
+
+ // 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;
@@ -10860,7 +11556,8 @@ static void ImGui::NavUpdateWindowing()
if (new_nav_layer != g.NavLayer)
{
// Reinitialize navigation when entering menu bar with the Alt key (FIXME: could be a properly of the layer?)
- if (new_nav_layer == ImGuiNavLayer_Menu)
+ const bool preserve_layer_1_nav_id = (new_nav_window->DockNodeAsHost != NULL);
+ if (new_nav_layer == ImGuiNavLayer_Menu && !preserve_layer_1_nav_id)
g.NavWindow->NavLastIds[new_nav_layer] = 0;
NavRestoreLayer(new_nav_layer);
NavRestoreHighlightAfterMove();
@@ -10875,6 +11572,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)";
}
@@ -10889,7 +11588,7 @@ void ImGui::NavUpdateWindowingOverlay()
if (g.NavWindowingListWindow == NULL)
g.NavWindowingListWindow = FindWindowByName("###NavWindowingList");
- const ImGuiViewport* viewport = 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->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
@@ -10983,6 +11682,7 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
// We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
// Rely on keeping other window->LastItemXXX fields intact.
source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect);
+ KeepAliveID(source_id);
bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id);
if (is_hovered && g.IO.MouseClicked[mouse_button])
{
@@ -11114,7 +11814,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))
@@ -11143,13 +11843,16 @@ bool ImGui::BeginDragDropTarget()
if (!(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect))
return false;
ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
- if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow || window->SkipItems)
+ if (hovered_window == NULL || window->RootWindowDockTree != hovered_window->RootWindowDockTree || window->SkipItems)
return false;
const ImRect& display_rect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? g.LastItemData.DisplayRect : g.LastItemData.Rect;
ImGuiID id = g.LastItemData.ID;
if (id == 0)
+ {
id = window->GetIDFromRectangle(display_rect);
+ KeepAliveID(id);
+ }
if (g.DragDropPayload.SourceId == id)
return false;
@@ -11563,6 +12266,20 @@ ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name)
return CreateNewWindowSettings(name);
}
+void ImGui::AddSettingsHandler(const ImGuiSettingsHandler* handler)
+{
+ ImGuiContext& g = *GImGui;
+ IM_ASSERT(FindSettingsHandler(handler->TypeName) == NULL);
+ g.SettingsHandlers.push_back(*handler);
+}
+
+void ImGui::RemoveSettingsHandler(const char* type_name)
+{
+ ImGuiContext& g = *GImGui;
+ if (ImGuiSettingsHandler* handler = FindSettingsHandler(type_name))
+ g.SettingsHandlers.erase(handler);
+}
+
ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
{
ImGuiContext& g = *GImGui;
@@ -11719,9 +12436,15 @@ static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*,
ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
int x, y;
int i;
- 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, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); }
+ ImU32 u1;
+ 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->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; }
+ else if (sscanf(line, "ClassId=0x%X", &u1) == 1) { settings->ClassId = u1; }
}
// Apply to existing windows (if any)
@@ -11755,9 +12478,14 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl
window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
}
IM_ASSERT(settings->ID == window->ID);
- settings->Pos = ImVec2ih(window->Pos);
+ settings->Pos = ImVec2ih(window->Pos - window->ViewportPos);
settings->Size = ImVec2ih(window->SizeFull);
-
+ settings->ViewportId = window->ViewportId;
+ settings->ViewportPos = ImVec2ih(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;
}
@@ -11767,9 +12495,26 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl
{
const char* settings_name = settings->GetName();
buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
- buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
- buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
+ if (settings->ViewportId != 0 && settings->ViewportId != ImGui::IMGUI_VIEWPORT_DEFAULT_ID)
+ {
+ 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)
+ buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
+ if (settings->Size.x != 0 || settings->Size.y != 0)
+ buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
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
+ buf->appendf("DockId=0x%08X,%d\n", settings->DockId, settings->DockOrder);
+ if (settings->ClassId != 0)
+ buf->appendf("ClassId=0x%08X\n", settings->ClassId);
+ }
buf->append("\n");
}
}
@@ -11779,8 +12524,28 @@ static void WindowSettingsHandler_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]
-// (this section is more complete in the 'docking' branch)
+// - UpdateViewportsEndFrame() [Internal]
+// - AddUpdateViewport() [Internal]
+// - WindowSelectViewport() [Internal]
+// - WindowSyncOwnedViewport() [Internal]
+// - UpdatePlatformWindows()
+// - RenderPlatformWindowsDefault()
+// - FindPlatformMonitorForPos() [Internal]
+// - FindPlatformMonitorForRect() [Internal]
+// - UpdateViewportPlatformMonitor() [Internal]
+// - DestroyPlatformWindow() [Internal]
+// - DestroyPlatformWindows()
//-----------------------------------------------------------------------------
ImGuiViewport* ImGui::GetMainViewport()
@@ -11789,36 +12554,4772 @@ 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;
+ 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.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);
+
+ // 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);
+}
+
+void ImGui::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);
+}
+
+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.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)
+ if (!window->DockIsActive)
+ 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;
+}
+
+static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport)
+{
+ ImGuiContext& g = *GImGui;
+ 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;
+ 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];
+ if (window_behind == window)
+ break;
+ if (window_behind->WasActive && window_behind->ViewportOwned && !(window_behind->Flags & ImGuiWindowFlags_ChildWindow))
+ if (window_behind->Viewport->GetMainRect().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;
+}
+
+// FIXME: handle 0 to N host viewports
+static bool ImGui::UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window)
+{
+ ImGuiContext& g = *GImGui;
+ return UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]);
+}
+
+// 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)
+{
+ 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)
+{
+ 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 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.
+ImGuiViewportP* ImGui::FindHoveredViewportFromPlatformWindowStack(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 | ImGuiViewportFlags_Minimized)) && viewport->GetMainRect().Contains(mouse_platform_pos))
+ if (best_candidate == NULL || best_candidate->LastFrontMostStampCount < viewport->LastFrontMostStampCount)
+ best_candidate = viewport;
+ }
+ return best_candidate;
+}
+
// Update viewports and monitor infos
+// 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;
- IM_ASSERT(g.Viewports.Size == 1);
+ 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)
+ const bool viewports_enabled = (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) != 0;
+ if (viewports_enabled)
+ {
+ 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);
+ if (minimized)
+ viewport->Flags |= ImGuiViewportFlags_Minimized;
+ else
+ viewport->Flags &= ~ImGuiViewportFlags_Minimized;
+ }
+ }
+ }
- // Update main viewport with current platform position.
+ // Create/update main viewport with current platform position.
// FIXME-VIEWPORT: Size is driven by backend/user code for backward-compatibility but we should aim to make this more consistent.
ImGuiViewportP* main_viewport = g.Viewports[0];
- main_viewport->Flags = ImGuiViewportFlags_IsPlatformWindow | ImGuiViewportFlags_OwnedByApp;
- main_viewport->Pos = ImVec2(0.0f, 0.0f);
- main_viewport->Size = g.IO.DisplaySize;
+ IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID);
+ IM_ASSERT(main_viewport->Window == NULL);
+ 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_OwnedByApp | ImGuiViewportFlags_CanHostOtherWindows);
+ g.CurrentDpiScale = 0.0f;
+ g.CurrentViewport = NULL;
+ g.MouseViewport = NULL;
for (int n = 0; n < g.Viewports.Size; n++)
{
ImGuiViewportP* viewport = g.Viewports[n];
+ viewport->Idx = n;
- // Lock down space taken by menu bars and status bars, reset the offset for fucntions like BeginMainMenuBar() to alter them again.
+ // Erase unused viewports
+ if (n > 0 && viewport->LastFrameActive < g.FrameCount - 2)
+ {
+ DestroyViewport(viewport);
+ n--;
+ continue;
+ }
+
+ const bool platform_funcs_available = viewport->PlatformWindowCreated;
+ 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.
+ 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)
+ viewport->Size = viewport->LastPlatformSize = g.PlatformIO.Platform_GetWindowSize(viewport);
+ }
+ }
+
+ // Update/copy monitor info
+ UpdateViewportPlatformMonitor(viewport);
+
+ // Lock down space taken by menu bars and status bars, reset the offset for functions like BeginMainMenuBar() to alter them again.
viewport->WorkOffsetMin = viewport->BuildWorkOffsetMin;
viewport->WorkOffsetMax = viewport->BuildWorkOffsetMax;
viewport->BuildWorkOffsetMin = viewport->BuildWorkOffsetMax = 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;
+
+ // 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))
+ TranslateWindowsInViewport(viewport, viewport->LastPos, viewport->Pos);
+
+ // 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;
+ }
+
+ // 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;
+ 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))
+ 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) 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)
+ 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)
+ // (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;
+
+ // 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.
+ // 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;
+ 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.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);
+
+ flags |= ImGuiViewportFlags_IsPlatformWindow;
+ if (window != NULL)
+ {
+ if (g.MovingWindow && g.MovingWindow->RootWindowDockTree == 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)
+ {
+ // 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 || viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID)
+ viewport->Size = size;
+ viewport->Flags = flags | (viewport->Flags & ImGuiViewportFlags_Minimized); // Preserve existing flags
+ }
+ else
+ {
+ // New viewport
+ viewport = IM_NEW(ImGuiViewportP)();
+ viewport->ID = id;
+ 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_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
+ 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);
+
+ // 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->LastFrameActive = g.FrameCount;
+ viewport->UpdateWorkRect();
+ IM_ASSERT(window == NULL || viewport->ID == window->ID);
+
+ if (window != NULL)
+ window->ViewportOwned = true;
+
+ 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)
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiWindowFlags flags = window->Flags;
+ window->ViewportAllowPlatformMonitorExtend = -1;
+
+ // Restore main viewport if multi-viewport is not supported by the backend
+ ImGuiViewportP* main_viewport = (ImGuiViewportP*)(void*)GetMainViewport();
+ if (!(g.ConfigFlagsCurrFrame & 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.Flags & ImGuiNextWindowDataFlags_HasViewport) == 0)
+ {
+ // By default inherit from parent window
+ 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
+ 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);
+ }
+ }
+
+ bool lock_viewport = false;
+ if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasViewport)
+ {
+ // 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))
+ {
+ // 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;
+ }
+ else if (GetWindowAlwaysWantOwnViewport(window))
+ {
+ window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None);
+ }
+ 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);
+ }
+ else
+ {
+ // Merge into host viewport?
+ // We cannot test window->ViewportOwned as it set lower in the function.
+ // 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);
+ }
+
+ // Fallback: merge in default viewport if z-order matches, otherwise create a new viewport
+ 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)
+ {
+ 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 | ImGuiWindowFlags_ChildMenu))) && (!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) && 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;
+ if ((window->Flags & ImGuiWindowFlags_DockNodeHost) && window->Viewport->LastFrameActive < g.FrameCount && will_be_visible)
+ {
+ // Steal/transfer ownership
+ 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;
+ }
+ else if (!UpdateTryMergeWindowIntoHostViewports(window)) // 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;
+ }
+ }
+
+ // 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;
+}
+
+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.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;
+
+ // Update parent viewport ID
+ // (the !IsFallbackWindow test mimic the one done in WindowSelectViewport())
+ 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;
+ else
+ window->Viewport->ParentViewportId = g.IO.ConfigViewportsNoDefaultParent ? 0 : IMGUI_VIEWPORT_DEFAULT_ID;
+}
+
+// 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.ConfigFlagsCurrFrame & 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##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)
+ {
+ 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_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);
+ 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 backends)
+ 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 backend 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.ViewportFrontMostStampCount)
+ viewport->LastFrontMostStampCount = ++g.ViewportFrontMostStampCount;
+ }
+
+ // Clear request flags
+ viewport->ClearRequestFlags();
+ }
+
+ // Update our implicit z-order knowledge of platform windows, which is used when the backend cannot provide io.MouseHoveredViewport.
+ // When setting Platform_GetWindowFocus, it is expected that the platform backend 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;
+ for (int n = 0; n < g.Viewports.Size && focused_viewport == NULL; n++)
+ {
+ ImGuiViewportP* viewport = g.Viewports[n];
+ if (viewport->PlatformWindowCreated)
+ if (g.PlatformIO.Platform_GetWindowFocus(viewport))
+ focused_viewport = viewport;
+ }
+
+ // Store a tag so we can infer z-order easily from all our windows
+ // 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;
+ }
+ }
+}
+
+// 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++)
+// 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++)
+// 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)
+{
+ // 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 (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);
+ }
+}
+
+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
+// 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);
+ 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;
+}
+
+// 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->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;
+ 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);
+
+ // Don't clear PlatformWindowCreated for the main viewport, as we initially set that up to true in Initialize()
+ // The righter way may be to leave it to the backend to set this flag all-together, and made the flag public.
+ if (viewport->ID != IMGUI_VIEWPORT_DEFAULT_ID)
+ viewport->PlatformWindowCreated = false;
+ }
+ else
+ {
+ IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL);
+ }
+ viewport->RendererUserData = viewport->PlatformUserData = viewport->PlatformHandle = NULL;
+ 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 backend
+ // to clear any data they may have stored in e.g. PlatformUserData, RendererUserData.
+ // It is convenient for the platform backend 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 backend 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
//-----------------------------------------------------------------------------
+// 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, DockSpaceOverViewport)
+// Docking: Builder Functions
+// Docking: Begin/End Support Functions (called from Begin/End)
+// Docking: Settings
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// Typical Docking call flow: (root level is generally public API):
+//-----------------------------------------------------------------------------
+// - NewFrame() new dear imgui frame
+// | DockContextNewFrameUpdateUndocking() - process queued undocking requests
+// | - DockContextProcessUndockWindow() - process one window undocking request
+// | - DockContextProcessUndockNode() - process one whole node undocking request
+// | DockContextNewFrameUpdateUndocking() - process queue docking requests, create floating dock nodes
+// | - update g.HoveredDockNode - [debug] update node hovered by mouse
+// | - DockContextProcessDock() - process one docking request
+// | - DockNodeUpdate()
+// | - DockNodeUpdateForRootNode()
+// | - DockNodeUpdateFlagsAndCollapse()
+// | - 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()
+//-----------------------------------------------------------------------------
+// - EndFrame()
+// | DockContextEndFrame()
+//-----------------------------------------------------------------------------
-// (this section is filled in the 'docking' branch)
+//-----------------------------------------------------------------------------
+// Docking: Internal Types
+//-----------------------------------------------------------------------------
+// - ImGuiDockRequestType
+// - ImGuiDockRequest
+// - ImGuiDockPreviewData
+// - ImGuiDockNodeSettings
+// - ImGuiDockContext
+//-----------------------------------------------------------------------------
+
+enum ImGuiDockRequestType
+{
+ ImGuiDockRequestType_None = 0,
+ ImGuiDockRequestType_Dock,
+ ImGuiDockRequestType_Undock,
+ ImGuiDockRequestType_Split // Split is the same as Dock but without a DockPayload
+};
+
+struct ImGuiDockRequest
+{
+ ImGuiDockRequestType Type;
+ 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;
+ ImGuiWindow* UndockTargetWindow;
+ ImGuiDockNode* UndockTargetNode;
+
+ ImGuiDockRequest()
+ {
+ Type = ImGuiDockRequestType_None;
+ DockTargetWindow = DockPayload = UndockTargetWindow = NULL;
+ DockTargetNode = UndockTargetNode = NULL;
+ DockSplitDir = ImGuiDir_None;
+ DockSplitRatio = 0.5f;
+ DockSplitOuter = 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; 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)
+struct ImGuiDockNodeSettings
+{
+ ImGuiID ID;
+ ImGuiID ParentNodeId;
+ ImGuiID ParentWindowId;
+ 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() { memset(this, 0, sizeof(*this)); SplitAxis = ImGuiAxis_None; }
+};
+
+//-----------------------------------------------------------------------------
+// Docking: Forward Declarations
+//-----------------------------------------------------------------------------
+
+namespace ImGui
+{
+ // ImGuiDockContext
+ static ImGuiDockNode* DockContextAddNode(ImGuiContext* ctx, ImGuiID id);
+ 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);
+ 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
+
+ // ImGuiDockNode
+ 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);
+ 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);
+ 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 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, 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; }
+ static int DockNodeGetTabOrder(ImGuiWindow* window);
+
+ // 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, ImGuiDockNode* only_write_to_single_node = NULL);
+ static void DockNodeTreeUpdateSplitter(ImGuiDockNode* node);
+ static ImGuiDockNode* DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos);
+ static ImGuiDockNode* DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node);
+
+ // Settings
+ 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);
+ static void DockSettingsHandler_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf);
+}
+
+//-----------------------------------------------------------------------------
+// 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 settings data.
+//-----------------------------------------------------------------------------
+// - DockContextInitialize()
+// - DockContextShutdown()
+// - DockContextClearNodes()
+// - DockContextRebuildNodes()
+// - DockContextNewFrameUpdateUndocking()
+// - DockContextNewFrameUpdateDocking()
+// - DockContextEndFrame()
+// - DockContextFindNodeByID()
+// - DockContextBindNodeToWindow()
+// - DockContextGenNodeID()
+// - DockContextAddNode()
+// - DockContextRemoveNode()
+// - ImGuiDockContextPruneNodeData
+// - DockContextPruneUnusedSettingsNodes()
+// - DockContextBuildNodesFromSettings()
+// - DockContextBuildAddWindowsToNodes()
+//-----------------------------------------------------------------------------
+
+void ImGui::DockContextInitialize(ImGuiContext* ctx)
+{
+ ImGuiContext& g = *ctx;
+
+ // Add .ini handle for persistent docking data
+ ImGuiSettingsHandler ini_handler;
+ ini_handler.TypeName = "Docking";
+ ini_handler.TypeHash = ImHashStr("Docking");
+ 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);
+}
+
+void ImGui::DockContextShutdown(ImGuiContext* 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);
+}
+
+void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_settings_refs)
+{
+ IM_UNUSED(ctx);
+ IM_ASSERT(ctx == GImGui);
+ DockBuilderRemoveNodeDockedWindows(root_id, clear_settings_refs);
+ DockBuilderRemoveNodeChildNodes(root_id);
+}
+
+// [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");
+ ImGuiDockContext* dc = &ctx->DockContext;
+ SaveIniSettingsToMemory();
+ ImGuiID root_id = 0; // Rebuild all
+ DockContextClearNodes(ctx, root_id, false);
+ DockContextBuildNodesFromSettings(ctx, dc->NodesSettings.Data, dc->NodesSettings.Size);
+ DockContextBuildAddWindowsToNodes(ctx, root_id);
+}
+
+// Docking context update function, called by NewFrame()
+void ImGui::DockContextNewFrameUpdateUndocking(ImGuiContext* ctx)
+{
+ ImGuiContext& g = *ctx;
+ ImGuiDockContext* dc = &ctx->DockContext;
+ if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
+ {
+ if (dc->Nodes.Data.Size > 0 || dc->Requests.Size > 0)
+ 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())
+ {
+ DockBuilderRemoveNodeChildNodes(node->ID);
+ //dc->WantFullRebuild = true;
+ }
+
+ // Process full rebuild
+#if 0
+ if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C)))
+ dc->WantFullRebuild = true;
+#endif
+ if (dc->WantFullRebuild)
+ {
+ DockContextRebuildNodes(ctx);
+ dc->WantFullRebuild = false;
+ }
+
+ // 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];
+ 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()
+void ImGui::DockContextNewFrameUpdateDocking(ImGuiContext* ctx)
+{
+ ImGuiContext& g = *ctx;
+ ImGuiDockContext* dc = &ctx->DockContext;
+ if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
+ return;
+
+ // [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)
+ {
+ if (hovered_window->DockNodeAsHost)
+ g.HoveredDockNode = DockNodeTreeFindVisibleNodeByPos(hovered_window->DockNodeAsHost, g.IO.MousePos);
+ else if (hovered_window->RootWindow->DockNode)
+ g.HoveredDockNode = hovered_window->RootWindow->DockNode;
+ }
+
+ // Process Docking requests
+ 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 < dc->Nodes.Data.Size; n++)
+ if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
+ if (node->IsFloatingNode())
+ 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);
+}
+
+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.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++;
+ 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)
+ 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);
+ return node;
+}
+
+static void ImGui::DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node)
+{
+ ImGuiContext& g = *ctx;
+ ImGuiDockContext* dc = &ctx->DockContext;
+
+ 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);
+
+ if (node->HostWindow)
+ node->HostWindow->DockNodeAsHost = NULL;
+
+ 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]);
+ DockNodeTreeMerge(&g, parent_node, sibling_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);
+ }
+}
+
+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);
+}
+
+// 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;
+ ImGuiDockContextPruneNodeData() { CountWindows = CountChildWindows = CountChildNodes = 0; RootId = 0; }
+};
+
+// Garbage collect unused nodes (run once at init time)
+static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx)
+{
+ ImGuiContext& g = *ctx;
+ ImGuiDockContext* dc = &ctx->DockContext;
+ IM_ASSERT(g.Windows.Size == 0);
+
+ ImPool<ImGuiDockContextPruneNodeData> pool;
+ pool.Reserve(dc->NodesSettings.Size);
+
+ // Count child nodes and compute RootID
+ for (int settings_n = 0; settings_n < dc->NodesSettings.Size; 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)
+ 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->NodesSettings.Size; settings_n++)
+ {
+ ImGuiDockNodeSettings* settings = &dc->NodesSettings[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
+ // We guard against the possibility of an invalid .ini file (RootID may point to a missing node)
+ for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
+ if (ImGuiID dock_id = settings->DockId)
+ if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(dock_id))
+ {
+ data->CountWindows++;
+ if (ImGuiDockContextPruneNodeData* data_root = (data->RootId == dock_id) ? data : pool.GetByKey(data->RootId))
+ data_root->CountChildWindows++;
+ }
+
+ // Prune
+ for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++)
+ {
+ ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n];
+ ImGuiDockContextPruneNodeData* data = pool.GetByKey(settings->ID);
+ if (data->CountWindows > 1)
+ continue;
+ 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_root->CountChildWindows == 0);
+ if (remove)
+ {
+ IMGUI_DEBUG_LOG_DOCKING("DockContextPruneUnusedSettingsNodes: Prune 0x%08X\n", settings->ID);
+ DockSettingsRemoveNodeReferences(&settings->ID, 1);
+ settings->ID = 0;
+ }
+ }
+}
+
+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++)
+ {
+ ImGuiDockNodeSettings* settings = &node_settings_array[node_n];
+ if (settings->ID == 0)
+ continue;
+ ImGuiDockNode* node = DockContextAddNode(ctx, settings->ID);
+ 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);
+ 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)
+ node->ParentNode->ChildNodes[1] = node;
+ node->SelectedTabId = settings->SelectedTabId;
+ node->SplitAxis = (ImGuiAxis)settings->SplitAxis;
+ 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.
+ char host_window_title[20];
+ ImGuiDockNode* root_node = DockNodeGetRootNode(node);
+ node->HostWindow = FindWindowByName(DockNodeGetHostWindowTitle(root_node, host_window_title, IM_ARRAYSIZE(host_window_title)));
+ }
+}
+
+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 = *ctx;
+ 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* 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);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// 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)
+{
+ IM_ASSERT(target != payload);
+ ImGuiDockRequest req;
+ req.Type = ImGuiDockRequestType_Dock;
+ req.DockTargetWindow = target;
+ req.DockTargetNode = target_node;
+ req.DockPayload = payload;
+ req.DockSplitDir = split_dir;
+ req.DockSplitRatio = split_ratio;
+ req.DockSplitOuter = split_outer;
+ ctx->DockContext.Requests.push_back(req);
+}
+
+void ImGui::DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window)
+{
+ ImGuiDockRequest req;
+ req.Type = ImGuiDockRequestType_Undock;
+ 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);
+}
+
+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;
+ IM_UNUSED(g);
+
+ 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;
+ ImGuiDockNode* payload_node = NULL;
+ 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->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->TabId;
+ }
+
+ // 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 (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 (node == NULL)
+ {
+ node = DockContextAddNode(ctx, 0);
+ node->Pos = target_window->Pos;
+ node->Size = target_window->Size;
+ if (target_window->DockNodeAsHost == NULL)
+ {
+ DockNodeAddWindow(node, target_window, true);
+ node->TabBar->Tabs[0].Flags &= ~ImGuiTabItemFlags_Unsorted;
+ target_window->DockIsActive = true;
+ }
+ }
+
+ ImGuiDir split_dir = req->DockSplitDir;
+ if (split_dir != ImGuiDir_None)
+ {
+ // 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;
+ 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;
+ }
+ node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_HiddenTabBar);
+
+ 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 (node->Windows.Size > 0 && node->TabBar == NULL)
+ {
+ DockNodeAddTabBar(node);
+ for (int n = 0; n < node->Windows.Size; n++)
+ TabBarAddTab(node->TabBar, ImGuiTabItemFlags_None, node->Windows[n]);
+ }
+
+ if (payload_node != NULL)
+ {
+ // Transfer full payload node (with 1+ child windows or child nodes)
+ if (payload_node->IsSplitNode())
+ {
+ 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.
+ // 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 DockNodePreviewDockSetup() early on and never submitted.
+ ImGuiDockNode* visible_node = payload_node->OnlyNodeWithWindows;
+ if (visible_node->TabBar)
+ IM_ASSERT(visible_node->TabBar->Tabs.Size > 0);
+ DockNodeMoveWindows(node, visible_node);
+ DockNodeMoveWindows(visible_node, node);
+ DockSettingsRenameNodeReferences(node->ID, visible_node->ID);
+ }
+ if (node->IsCentralNode())
+ {
+ // 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);
+ 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);
+ node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_CentralNode);
+ last_focused_root_node->CentralNode = last_focused_node;
+ }
+
+ IM_ASSERT(node->Windows.Size == 0);
+ DockNodeMoveChildNodes(node, payload_node);
+ }
+ else
+ {
+ const ImGuiID payload_dock_id = payload_node->ID;
+ DockNodeMoveWindows(node, payload_node);
+ DockSettingsRenameNodeReferences(payload_dock_id, node->ID);
+ }
+ DockContextRemoveNode(ctx, payload_node, true);
+ }
+ else if (payload_window)
+ {
+ // Transfer single window
+ const ImGuiID payload_dock_id = payload_window->DockId;
+ node->VisibleWindow = payload_window;
+ DockNodeAddWindow(node, payload_window, true);
+ if (payload_dock_id != 0)
+ 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)
+ tab_bar->NextSelectedTabId = next_selected_id;
+ 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);
+ IM_UNUSED(ctx);
+ if (window->DockNode)
+ DockNodeRemoveWindow(window->DockNode, window, clear_persistent_docking_ref ? 0 : window->DockId);
+ else
+ window->DockId = 0;
+ window->Collapsed = false;
+ window->DockIsActive = false;
+ window->DockNodeIsVisible = window->DockTabIsVisible = false;
+ window->Size = window->SizeFull = FixLargeWindowsWhenUndocking(window->SizeFull, window->Viewport);
+
+ MarkIniSettingsDirty();
+}
+
+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);
+
+ 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);
+ 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++)
+ {
+ 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
+ {
+ // 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;
+ 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_DockNode;
+ node->Size = FixLargeWindowsWhenUndocking(node->Size, node->Windows[0]->Viewport);
+ node->WantMouseMove = true;
+ 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)
+{
+ // 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;
+}
+
+//-----------------------------------------------------------------------------
+// Docking: ImGuiDockNode
+//-----------------------------------------------------------------------------
+// - DockNodeGetTabOrder()
+// - DockNodeAddWindow()
+// - DockNodeRemoveWindow()
+// - DockNodeMoveChildNodes()
+// - DockNodeMoveWindows()
+// - DockNodeApplyPosSizeToWindows()
+// - DockNodeHideHostWindow()
+// - ImGuiDockNodeFindInfoResults
+// - DockNodeFindInfo()
+// - DockNodeFindWindowByID()
+// - DockNodeUpdateFlagsAndCollapse()
+// - DockNodeUpdateHasCentralNodeFlag()
+// - DockNodeUpdateVisibleFlag()
+// - DockNodeStartMouseMovingWindow()
+// - DockNodeUpdate()
+// - DockNodeUpdateWindowMenu()
+// - DockNodeBeginAmendTabBar()
+// - DockNodeEndAmendTabBar()
+// - DockNodeUpdateTabBar()
+// - DockNodeAddTabBar()
+// - DockNodeRemoveTabBar()
+// - DockNodeIsDropAllowedOne()
+// - DockNodeIsDropAllowed()
+// - DockNodeCalcTabBarLayout()
+// - DockNodeCalcSplitRects()
+// - DockNodeCalcDropRectsAndTestMousePos()
+// - DockNodePreviewDockSetup()
+// - DockNodePreviewDockRender()
+//-----------------------------------------------------------------------------
+
+ImGuiDockNode::ImGuiDockNode(ImGuiID id)
+{
+ ID = id;
+ SharedFlags = LocalFlags = LocalFlagsInWindows = MergedFlags = ImGuiDockNodeFlags_None;
+ ParentNode = ChildNodes[0] = ChildNodes[1] = NULL;
+ TabBar = NULL;
+ SplitAxis = ImGuiAxis_None;
+
+ State = ImGuiDockNodeState_Unknown;
+ LastBgColor = IM_COL32_WHITE;
+ HostWindow = VisibleWindow = NULL;
+ CentralNode = OnlyNodeWithWindows = NULL;
+ CountNodeWithWindows = 0;
+ LastFrameAlive = LastFrameActive = LastFrameFocused = -1;
+ LastFocusedNodeId = 0;
+ SelectedTabId = 0;
+ WantCloseTabId = 0;
+ AuthorityForPos = AuthorityForSize = ImGuiDataAuthority_DockNode;
+ AuthorityForViewport = ImGuiDataAuthority_Auto;
+ IsVisible = true;
+ IsFocused = HasCloseButton = HasWindowMenuButton = HasCentralNodeChild = false;
+ IsBgDrawnThisFrame = false;
+ WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false;
+}
+
+ImGuiDockNode::~ImGuiDockNode()
+{
+ IM_DELETE(TabBar);
+ TabBar = NULL;
+ ChildNodes[0] = ChildNodes[1] = NULL;
+}
+
+int ImGui::DockNodeGetTabOrder(ImGuiWindow* window)
+{
+ ImGuiTabBar* tab_bar = window->DockNode->TabBar;
+ if (tab_bar == NULL)
+ return -1;
+ ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, window->TabId);
+ 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;
+ if (window->DockNode)
+ {
+ // 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);
+ }
+ 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;
+ window->DockId = node->ID;
+ window->DockIsActive = (node->Windows.Size > 1);
+ window->DockTabWantClose = false;
+
+ // 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())
+ {
+ 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
+ if (add_to_tab_bar)
+ {
+ if (node->TabBar == NULL)
+ {
+ DockNodeAddTabBar(node);
+ node->TabBar->SelectedTabId = node->TabBar->NextSelectedTabId = node->SelectedTabId;
+
+ // Add existing windows
+ for (int n = 0; n < node->Windows.Size - 1; n++)
+ TabBarAddTab(node->TabBar, ImGuiTabItemFlags_None, node->Windows[n]);
+ }
+ TabBarAddTab(node->TabBar, ImGuiTabItemFlags_Unsorted, window);
+ }
+
+ 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;
+ IM_ASSERT(window->DockNode == node);
+ //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);
+
+ window->DockNode = NULL;
+ 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
+ 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;
+ }
+ if (!erased)
+ IM_ASSERT(erased);
+ if (node->VisibleWindow == window)
+ node->VisibleWindow = NULL;
+
+ // Remove tab and possibly tab bar
+ node->WantHiddenTabBarUpdate = true;
+ if (node->TabBar)
+ {
+ 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);
+ }
+
+ 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)
+ {
+ ImGuiWindow* remaining_window = node->Windows[0];
+ 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;
+ }
+ remaining_window->Collapsed = node->HostWindow->Collapsed;
+ }
+
+ // Update visibility immediately is required so the DockNodeUpdateRemoveInactiveChilds() processing can reflect changes up the tree
+ DockNodeUpdateVisibleFlag(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];
+ 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->SizeRef = src_node->SizeRef;
+ 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(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);
+
+ // 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++)
+ {
+ // 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();
+
+ if (!move_tab_bar && src_node->TabBar)
+ {
+ if (dst_node->TabBar)
+ dst_node->TabBar->SelectedTabId = src_node->TabBar->SelectedTabId;
+ DockNodeRemoveTabBar(src_node);
+ }
+}
+
+static void ImGui::DockNodeApplyPosSizeToWindows(ImGuiDockNode* node)
+{
+ for (int n = 0; n < node->Windows.Size; n++)
+ {
+ 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);
+ }
+}
+
+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)
+ DockNodeRemoveTabBar(node);
+}
+
+// Search function called once by root node in DockNodeUpdate()
+struct ImGuiDockNodeTreeInfo
+{
+ ImGuiDockNode* CentralNode;
+ ImGuiDockNode* FirstNodeWithWindows;
+ int CountNodesWithWindows;
+ //ImGuiWindowClass WindowClassForMerges;
+
+ ImGuiDockNodeTreeInfo() { memset(this, 0, sizeof(*this)); }
+};
+
+static void DockNodeFindInfo(ImGuiDockNode* node, ImGuiDockNodeTreeInfo* info)
+{
+ if (node->Windows.Size > 0)
+ {
+ if (info->FirstNodeWithWindows == NULL)
+ info->FirstNodeWithWindows = node;
+ info->CountNodesWithWindows++;
+ }
+ if (node->IsCentralNode())
+ {
+ 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.");
+ info->CentralNode = node;
+ }
+ if (info->CountNodesWithWindows > 1 && info->CentralNode != NULL)
+ return;
+ if (node->ChildNodes[0])
+ DockNodeFindInfo(node->ChildNodes[0], info);
+ if (node->ChildNodes[1])
+ DockNodeFindInfo(node->ChildNodes[1], info);
+}
+
+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::DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node)
+{
+ ImGuiContext& g = *GImGui;
+ IM_ASSERT(node->ParentNode == NULL || node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node);
+
+ // Inherit most flags
+ if (node->ParentNode)
+ 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'.
+ // 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])
+ DockNodeUpdateFlagsAndCollapse(node->ChildNodes[1]);
+
+ // 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++)
+ {
+ 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->WantCloseTabId == window->TabId) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument); // Submit all _expected_ closure from last frame
+ remove |= (window->DockTabWantClose);
+ if (remove)
+ {
+ 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--;
+ continue;
+ }
+
+ // 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();
+
+ // Auto-hide tab bar option
+ ImGuiDockNodeFlags node_flags = node->MergedFlags;
+ if (node->WantHiddenTabBarUpdate && node->Windows.Size == 1 && (node_flags & ImGuiDockNodeFlags_AutoHideTabBar) && !node->IsHiddenTabBar())
+ 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->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_HiddenTabBar);
+ else if (node->WantHiddenTabBarToggle)
+ node->SetLocalFlags(node->LocalFlags ^ ImGuiDockNodeFlags_HiddenTabBar);
+ node->WantHiddenTabBarToggle = false;
+
+ 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
+ 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);
+ node->IsVisible = is_visible;
+}
+
+static void ImGui::DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window)
+{
+ ImGuiContext& g = *GImGui;
+ IM_ASSERT(node->WantMouseMove == true);
+ 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;
+}
+
+// Update CentralNode, OnlyNodeWithWindows, LastFocusedNodeID. Copy window class.
+static void ImGui::DockNodeUpdateForRootNode(ImGuiDockNode* node)
+{
+ DockNodeUpdateFlagsAndCollapse(node);
+
+ // - 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!)
+ // 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;
+ node->CountNodeWithWindows = info.CountNodesWithWindows;
+ 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 = info.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;
+ }
+ }
+
+ 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)
+{
+ // 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;
+ IM_ASSERT(node->LastFrameActive != g.FrameCount);
+ node->LastFrameAlive = g.FrameCount;
+ node->IsBgDrawnThisFrame = false;
+
+ node->CentralNode = node->OnlyNodeWithWindows = NULL;
+ if (node->IsRootNode())
+ DockNodeUpdateForRootNode(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)
+ bool want_to_hide_host_window = false;
+ 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)
+ {
+ // Floating window pos/size is authoritative
+ 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)
+ FocusWindow(single_window);
+ if (node->HostWindow)
+ {
+ 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;
+ }
+ }
+ }
+
+ DockNodeHideHostWindow(node);
+ node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow;
+ node->WantCloseAll = false;
+ node->WantCloseTabId = 0;
+ node->HasCloseButton = node->HasWindowMenuButton = false;
+ node->LastFrameActive = g.FrameCount;
+
+ if (node->WantMouseMove && node->Windows.Size == 1)
+ DockNodeStartMouseMovingWindow(node, node->Windows[0]);
+ 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->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;
+ 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;
+ if (node->IsDockSpace())
+ {
+ // [Explicit root dockspace node]
+ IM_ASSERT(node->HostWindow);
+ host_window = node->HostWindow;
+ }
+ else
+ {
+ // [Automatic root or child nodes]
+ if (node->IsRootNode() && node->IsVisible)
+ {
+ ImGuiWindow* ref_window = (node->Windows.Size > 0) ? node->Windows[0] : NULL;
+
+ // Sync Pos
+ if (node->AuthorityForPos == ImGuiDataAuthority_Window && ref_window)
+ SetNextWindowPos(ref_window->Pos);
+ else if (node->AuthorityForPos == ImGuiDataAuthority_DockNode)
+ SetNextWindowPos(node->Pos);
+
+ // Sync Size
+ if (node->AuthorityForSize == ImGuiDataAuthority_Window && ref_window)
+ SetNextWindowSize(ref_window->SizeFull);
+ else if (node->AuthorityForSize == ImGuiDataAuthority_DockNode)
+ SetNextWindowSize(node->Size);
+
+ // Sync Collapsed
+ if (node->AuthorityForSize == ImGuiDataAuthority_Window && ref_window)
+ SetNextWindowCollapsed(ref_window->Collapsed);
+
+ // Sync Viewport
+ if (node->AuthorityForViewport == ImGuiDataAuthority_Window && ref_window)
+ SetNextWindowViewport(ref_window->ViewportId);
+
+ SetNextWindowClass(&node->WindowClass);
+
+ // Begin into the host window
+ 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 | 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();
+ beginned_into_host_window = true;
+
+ 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;
+
+ // 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);
+
+ node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Auto;
+ }
+ else if (node->ParentNode)
+ {
+ node->HostWindow = host_window = node->ParentNode->HostWindow;
+ node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Auto;
+ }
+ if (node->WantMouseMove && node->HostWindow)
+ DockNodeStartMouseMovingWindow(node, node->HostWindow);
+ }
+
+ // 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 (g.NavWindow && g.NavWindow->RootWindow->DockNode && g.NavWindow->RootWindow->ParentWindow == host_window)
+ node->LastFocusedNodeId = g.NavWindow->RootWindow->DockNode->ID;
+
+ // 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();
+ 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)
+ {
+ // 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
+ 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_HOVER_PADDING; }
+ if (hole_rect.Max.x < root_rect.Max.x) { hole_rect.Max.x -= WINDOWS_HOVER_PADDING; }
+ if (hole_rect.Min.y > root_rect.Min.y) { hole_rect.Min.y += WINDOWS_HOVER_PADDING; }
+ if (hole_rect.Max.y < root_rect.Max.y) { hole_rect.Max.y -= WINDOWS_HOVER_PADDING; }
+ //GetForegroundDrawList()->AddRect(hole_rect.Min, hole_rect.Max, IM_COL32(255, 0, 0, 255));
+ if (central_node_hole && !hole_rect.IsInverted())
+ {
+ SetWindowHitTestHole(host_window, 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);
+ }
+ }
+
+ // 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)
+ {
+ 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);
+ 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);
+ }
+
+ // Draw and populate Tab Bar
+ if (host_window)
+ host_window->DrawList->ChannelsSetCurrent(1);
+ if (host_window && node->Windows.Size > 0)
+ {
+ DockNodeUpdateTabBar(node, host_window);
+ }
+ else
+ {
+ node->WantCloseAll = false;
+ node->WantCloseTabId = 0;
+ node->IsFocused = false;
+ }
+ 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)
+ if (node->IsRootNode() && (g.MovingWindow == NULL || g.MovingWindow->RootWindowDockTree != host_window))
+ BeginDockableDragDropTarget(host_window);
+
+ // We update this after DockNodeUpdateTabBar()
+ 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])
+ DockNodeUpdate(node->ChildNodes[0]);
+ if (node->ChildNodes[1])
+ DockNodeUpdate(node->ChildNodes[1]);
+
+ // 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
+ 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.
+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 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;
+ ImGuiID ret_tab_id = 0;
+ 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("#WindowMenu"))
+ {
+ 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];
+ if (tab->Flags & ImGuiTabItemFlags_Button)
+ continue;
+ if (Selectable(tab_bar->GetTabName(tab), tab->ID == tab_bar->SelectedTabId))
+ ret_tab_id = tab->ID;
+ SameLine();
+ Text(" ");
+ }
+ }
+ EndPopup();
+ }
+ 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;
+ if (node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly)
+ return false;
+ Begin(node->HostWindow->Name);
+ PushOverrideID(node->ID);
+ bool ret = BeginTabBarEx(node->TabBar, node->TabBar->BarRect, node->TabBar->Flags, node);
+ IM_UNUSED(ret);
+ 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;
+ 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->WantCloseTabId && node_was_active;
+ 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->RootWindowDockTree && 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->IsNoTabBar())
+ {
+ node->VisibleWindow = (node->Windows.Size > 0) ? node->Windows[0] : NULL;
+ node->IsFocused = is_focused;
+ if (is_focused)
+ node->LastFrameFocused = g.FrameCount;
+ 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)
+ node->TabBar->VisibleTabId = node->VisibleWindow->TabId;
+ }
+ 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())
+ {
+ host_window->SkipItems = false;
+ host_window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
+ }
+
+ // 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)
+ {
+ DockNodeAddTabBar(node);
+ tab_bar = node->TabBar;
+ }
+
+ ImGuiID focus_tab_id = 0;
+ node->IsFocused = is_focused;
+
+ 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.
+ // 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))
+ focus_tab_id = tab_bar->NextSelectedTabId = tab_id;
+ is_focused |= node->IsFocused;
+ }
+
+ // Layout
+ ImRect title_bar_rect, tab_bar_rect;
+ ImVec2 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;
+ for (int window_n = 0; window_n < node->Windows.Size; window_n++)
+ {
+ ImGuiWindow* window = node->Windows[window_n];
+ if (TabBarFindTabByID(tab_bar, window->TabId) == NULL)
+ TabBarAddTab(tab_bar, ImGuiTabItemFlags_Unsorted, window);
+ }
+
+ // Title bar
+ if (is_focused)
+ node->LastFrameFocused = g.FrameCount;
+ ImU32 title_bar_col = GetColorU32(host_window->Collapsed ? ImGuiCol_TitleBgCollapsed : is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
+ 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
+ if (has_window_menu_button)
+ {
+ if (CollapseButton(host_window->GetID("#COLLAPSE"), window_menu_button_pos, node)) // == DockNodeGetWindowMenuButtonId(node)
+ OpenPopup("#WindowMenu");
+ if (IsItemActive())
+ focus_tab_id = tab_bar->SelectedTabId;
+ }
+
+ // 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--)
+ {
+ // 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;
+ }
+ 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);
+ }
+
+ // 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;
+ else if (tab_bar->Tabs.Size > tabs_count_old)
+ 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);
+ 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);
+ //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++)
+ {
+ ImGuiWindow* window = node->Windows[window_n];
+ if ((closed_all || closed_one == window->TabId) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument))
+ continue;
+ 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)
+ 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]);
+
+ // 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->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
+ window->DockTabItemStatusFlags = g.LastItemData.StatusFlags;
+ window->DockTabItemRect = g.LastItemData.Rect;
+
+ // 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->TabId;
+ }
+ }
+
+ // 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)
+ root_node->VisibleWindow = node->VisibleWindow;
+
+ // Close button (after VisibleWindow was updated)
+ // 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)
+ if (close_button_is_visible)
+ {
+ if (!close_button_is_enabled)
+ {
+ PushItemFlag(ImGuiItemFlags_Disabled, true);
+ 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))
+ {
+ 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)
+ {
+ PopStyleColor();
+ PopItemFlag();
+ }
+ }
+
+ // When clicking on the title bar outside of tabs, we still focus the selected tab for that node
+ // 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)
+ {
+ bool 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.
+ g.LastItemData.ID = title_bar_id;
+ SetItemAllowOverlap();
+ }
+ if (held)
+ {
+ if (IsMouseClicked(0))
+ focus_tab_id = tab_bar->SelectedTabId;
+
+ // Forward moving request to selected window
+ if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId))
+ StartMouseMovingWindowOrNode(tab->Window ? tab->Window : node->HostWindow, node, false);
+ }
+ }
+
+ // Forward focus from host node to selected window
+ //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->NextSelectedTabId)
+ focus_tab_id = tab_bar->NextSelectedTabId;
+
+ // Apply navigation focus
+ if (focus_tab_id != 0)
+ if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, focus_tab_id))
+ if (tab->Window)
+ {
+ FocusWindow(tab->Window);
+ NavInitWindow(tab->Window, false);
+ }
+
+ EndTabBar();
+ PopID();
+
+ // Restore SkipItems flag
+ if (!node->IsDockSpace())
+ {
+ host_window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
+ host_window->SkipItems = backup_skip_item;
+ }
+}
+
+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)
+ return false;
+
+ 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_class->ClassId != 0 && host_class->DockingAllowUnclassed && payload_class->ClassId == 0)
+ return true;
+ if (payload_class->ClassId != 0 && payload_class->DockingAllowUnclassed && host_class->ClassId == 0)
+ return true;
+ 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()) // FIXME-DOCK: Missing filtering
+ 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 (DockNodeIsDropAllowedOne(payload, host_window))
+ return true;
+ }
+ 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_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 += style.FramePadding.x;
+ r.Max.x -= style.FramePadding.x;
+ if (node->HasCloseButton)
+ {
+ 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 && style.WindowMenuButtonPosition == ImGuiDir_Left)
+ {
+ r.Min.x += button_sz + style.ItemInnerSpacing.x;
+ }
+ else if (node->HasWindowMenuButton && style.WindowMenuButtonPosition == ImGuiDir_Right)
+ {
+ 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; }
+ 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)
+{
+ 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] = IM_FLOOR(w_avail - size_new[axis]);
+ }
+ else
+ {
+ size_new[axis] = IM_FLOOR(w_avail * 0.5f);
+ size_old[axis] = IM_FLOOR(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::DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_r, bool outer_docking, ImVec2* test_mouse_pos)
+{
+ 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 - hs_h), ImFloor(parent.GetHeight() * 0.5f - 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); }
+
+ 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 = (*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);
+ 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(*test_mouse_pos);
+}
+
+// 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)
+{
+ 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);
+
+ // 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 dst_node_flags = host_node ? host_node->MergedFlags : host_window->WindowClass.DockNodeFlagsOverrideSet;
+ data->IsCenterAvailable = true;
+ if (is_outer_docking)
+ data->IsCenterAvailable = false;
+ else if (dst_node_flags & ImGuiDockNodeFlags_NoDocking)
+ 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?
+ data->IsCenterAvailable = false;
+ 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;
+ 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);
+ 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;
+
+ // 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 (DockNodeCalcDropRectsAndTestMousePos(data->FutureNode.Rect(), (ImGuiDir)dir, data->DropRectsDraw[dir+1], is_outer_docking, &g.IO.MousePos))
+ {
+ 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.ConfigDockingWithShift)
+ 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_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
+ 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);
+ }
+}
+
+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.
+ 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++] = GetForegroundDrawList(host_window->Viewport);
+ if (host_window->Viewport != root_payload->Viewport && !is_transparent_payload)
+ overlay_draw_lists[overlay_draw_lists_count++] = GetForegroundDrawList(root_payload->Viewport);
+
+ // Draw main preview rectangle
+ 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);
+ 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, 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)
+ 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;
+ DockNodeCalcTabBarLayout(&data->FutureNode, NULL, &tab_bar_rect, NULL, NULL);
+ ImVec2 tab_pos = tab_bar_rect.Min;
+ if (host_node && host_node->TabBar)
+ {
+ 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;
+ }
+ 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);
+ 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++)
+ {
+ // 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;
+
+ // 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;
+ 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);
+ 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_window->Name, 0, 0, false, NULL, NULL);
+ if (!tab_bar_rect.Contains(tab_bb))
+ overlay_draw_lists[overlay_n]->PopClipRect();
+ }
+ PopStyleColor();
+ }
+ }
+
+ // 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);
+ }
+ }
+
+ // Stop after ImGuiDir_None
+ if ((host_node && (host_node->MergedFlags & ImGuiDockNodeFlags_NoSplit)) || g.IO.ConfigDockingNoSplit)
+ return;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// 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)
+{
+ ImGuiContext& g = *GImGui;
+ IM_ASSERT(split_axis != ImGuiAxis_None);
+
+ 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, 0);
+ child_1->ParentNode = parent_node;
+
+ ImGuiDockNode* child_inheritor = (split_inheritor_child_idx == 0) ? child_0 : child_1;
+ 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;
+ 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] - 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;
+ 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);
+ 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)
+ 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_;
+ child_0->UpdateMergedFlags();
+ child_1->UpdateMergedFlags();
+ parent_node->UpdateMergedFlags();
+ if (child_inheritor->IsCentralNode())
+ DockNodeGetRootNode(parent_node)->CentralNode = child_inheritor;
+}
+
+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(merge_lead_child == child_0 || merge_lead_child == child_1);
+ 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);
+ }
+ 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);
+ if (child_0)
+ {
+ DockNodeMoveWindows(parent_node, child_0); // Generally only 1 of the 2 child node will have windows
+ DockSettingsRenameNodeReferences(child_0->ID, parent_node->ID);
+ }
+ if (child_1)
+ {
+ DockNodeMoveWindows(parent_node, child_1);
+ DockSettingsRenameNodeReferences(child_1->ID, parent_node->ID);
+ }
+ DockNodeApplyPosSizeToWindows(parent_node);
+ 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;
+
+ // Flags transfer
+ 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)
+ {
+ 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)
+// (Depth-first, Pre-Order)
+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_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;
+ node->Size = size;
+ }
+
+ if (node->IsLeafNode())
+ 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;
+
+ 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.
+ 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)
+ {
+ 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_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 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_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->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]);
+ }
+ else
+ {
+ // 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]);
+ }
+
+ child_1_pos[axis] += spacing + child_0_size[axis];
+ }
+
+ 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_recurse)
+ DockNodeTreeUpdatePosSize(child_1, child_1_pos, child_1_size);
+}
+
+static void DockNodeTreeUpdateSplitterFindTouchingNode(ImGuiDockNode* node, ImGuiAxis axis, int side, ImVector<ImGuiDockNode*>* touching_nodes)
+{
+ if (node->IsLeafNode())
+ {
+ 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);
+}
+
+// (Depth-First, Pre-Order)
+void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node)
+{
+ if (node->IsLeafNode())
+ return;
+
+ ImGuiContext& g = *GImGui;
+
+ ImGuiDockNode* child_0 = node->ChildNodes[0];
+ ImGuiDockNode* child_1 = node->ChildNodes[1];
+ if (child_0->IsVisible && child_1->IsVisible)
+ {
+ // 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];
+ //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->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))
+ {
+ ImGuiWindow* window = g.CurrentWindow;
+ window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator), g.Style.FrameRounding);
+ }
+ else
+ {
+ //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);
+
+ // Find resizing limits by gathering list of nodes that are touching the splitter line.
+ ImVector<ImGuiDockNode*> 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 touching nodes & 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);
+ }
+ */
+ }
+
+ // 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];
+ 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)
+ {
+ 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.
+ 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 ? 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)
+ {
+ 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;
+ }
+ }
+
+ 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::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::DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos)
+{
+ if (!node->IsVisible)
+ return NULL;
+
+ 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);
+ if (!inside)
+ return NULL;
+
+ if (node->IsLeafNode())
+ return node;
+ if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(node->ChildNodes[0], pos))
+ return hovered_node;
+ if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(node->ChildNodes[1], pos))
+ return hovered_node;
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport)
+//-----------------------------------------------------------------------------
+// - SetWindowDock() [Internal]
+// - 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
+ if (cond && (window->SetWindowDockAllowFlags & cond) == 0)
+ return;
+ window->SetWindowDockAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
+
+ 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)
+ {
+ IM_ASSERT(new_node->CentralNode->IsCentralNode());
+ 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;
+}
+
+// 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.
+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 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.
+ // 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);
+ IM_ASSERT(id != 0);
+ ImGuiDockNode* node = DockContextFindNodeByID(ctx, id);
+ if (!node)
+ {
+ IMGUI_DEBUG_LOG_DOCKING("DockSpace: dockspace node 0x%08X created\n", id);
+ node = DockContextAddNode(ctx, id);
+ 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);
+ 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
+ // 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->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_DockSpace);
+ return id;
+ }
+ 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)
+ {
+ node->LastFrameAlive = g.FrameCount;
+ return id;
+ }
+
+ 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);
+ IM_ASSERT(size.x > 0.0f && size.y > 0.0f);
+
+ node->Pos = window->DC.CursorPos;
+ node->Size = node->SizeRef = size;
+ SetNextWindowPos(node->Pos);
+ 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: 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);
+
+ PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.0f);
+ Begin(title, NULL, window_flags);
+ PopStyleVar();
+
+ ImGuiWindow* host_window = g.CurrentWindow;
+ DockNodeSetupHostWindow(node, host_window);
+ host_window->ChildId = window->GetID(title);
+ 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->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_CentralNode);
+
+ // Update the node
+ DockNodeUpdate(node);
+
+ End();
+ ItemSize(size);
+ return id;
+}
+
+// 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.
+// 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(const ImGuiViewport* viewport, ImGuiDockNodeFlags dockspace_flags, const ImGuiWindowClass* window_class)
+{
+ if (viewport == NULL)
+ viewport = GetMainViewport();
+
+ SetNextWindowPos(viewport->WorkPos);
+ SetNextWindowSize(viewport->WorkSize);
+ 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_PassthruCentralNode)
+ host_window_flags |= ImGuiWindowFlags_NoBackground;
+
+ char label[32];
+ ImFormatString(label, IM_ARRAYSIZE(label), "DockSpaceViewport_%08X", viewport->ID);
+
+ 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 = GetID("DockSpace");
+ DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags, window_class);
+ End();
+
+ return dockspace_id;
+}
+
+//-----------------------------------------------------------------------------
+// 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)
+{
+ // We don't preserve relative order of multiple docked windows (by clearing DockOrder back to -1)
+ ImGuiID window_id = ImHashStr(window_name);
+ if (ImGuiWindow* window = FindWindowByID(window_id))
+ {
+ // Apply to created window
+ SetWindowDock(window, node_id, ImGuiCond_Always);
+ window->DockOrder = -1;
+ }
+ else
+ {
+ // Apply to settings
+ ImGuiWindowSettings* settings = FindWindowSettings(window_id);
+ if (settings == NULL)
+ settings = CreateNewWindowSettings(window_name);
+ settings->DockId = node_id;
+ settings->DockOrder = -1;
+ }
+}
+
+ImGuiDockNode* ImGui::DockBuilderGetNode(ImGuiID node_id)
+{
+ ImGuiContext* ctx = GImGui;
+ return DockContextFindNodeByID(ctx, node_id);
+}
+
+void ImGui::DockBuilderSetNodePos(ImGuiID node_id, ImVec2 pos)
+{
+ ImGuiContext* ctx = GImGui;
+ ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_id);
+ if (node == NULL)
+ return;
+ node->Pos = pos;
+ node->AuthorityForPos = ImGuiDataAuthority_DockNode;
+}
+
+void ImGui::DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size)
+{
+ ImGuiContext* ctx = GImGui;
+ 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;
+}
+
+// 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.
+// - Existing node with a same id will be removed.
+ImGuiID ImGui::DockBuilderAddNode(ImGuiID id, ImGuiDockNodeFlags flags)
+{
+ ImGuiContext* ctx = GImGui;
+
+ if (id != 0)
+ DockBuilderRemoveNode(id);
+
+ ImGuiDockNode* node = NULL;
+ if (flags & ImGuiDockNodeFlags_DockSpace)
+ {
+ DockSpace(id, ImVec2(0, 0), (flags & ~ImGuiDockNodeFlags_DockSpace) | ImGuiDockNodeFlags_KeepAliveOnly);
+ node = DockContextFindNodeByID(ctx, id);
+ }
+ else
+ {
+ node = DockContextAddNode(ctx, id);
+ node->SetLocalFlags(flags);
+ }
+ node->LastFrameAlive = ctx->FrameCount; // Set this otherwise BeginDocked will undock during the same frame.
+ return node->ID;
+}
+
+void ImGui::DockBuilderRemoveNode(ImGuiID node_id)
+{
+ ImGuiContext* ctx = GImGui;
+ ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_id);
+ if (node == NULL)
+ 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);
+}
+
+// root_id = 0 to remove all, root_id != 0 to remove child of given node.
+void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id)
+{
+ ImGuiContext* ctx = GImGui;
+ ImGuiDockContext* dc = &ctx->DockContext;
+
+ ImGuiDockNode* root_node = root_id ? DockContextFindNodeByID(ctx, root_id) : NULL;
+ if (root_id && root_node == NULL)
+ return;
+ bool has_central_node = false;
+
+ 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<ImGuiDockNode*> 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->IsCentralNode())
+ has_central_node = true;
+ 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);
+ }
+ }
+
+ // 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->AuthorityForPos = backup_root_node_authority_for_pos;
+ root_node->AuthorityForSize = backup_root_node_authority_for_size;
+ }
+
+ // Apply to settings
+ for (ImGuiWindowSettings* settings = ctx->SettingsWindows.begin(); settings != NULL; settings = ctx->SettingsWindows.next_chunk(settings))
+ if (ImGuiID window_settings_dock_id = settings->DockId)
+ for (int n = 0; n < nodes_to_remove.Size; n++)
+ if (nodes_to_remove[n]->ID == window_settings_dock_id)
+ {
+ settings->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_central_node)
+ {
+ root_node->CentralNode = root_node;
+ root_node->SetLocalFlags(root_node->LocalFlags | ImGuiDockNodeFlags_CentralNode);
+ }
+}
+
+void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiID root_id, bool clear_settings_refs)
+{
+ // Clear references in settings
+ ImGuiContext* ctx = GImGui;
+ ImGuiContext& g = *ctx;
+ if (clear_settings_refs)
+ {
+ for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
+ {
+ 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)
+ {
+ const ImGuiID backup_dock_id = window->DockId;
+ IM_UNUSED(backup_dock_id);
+ DockContextProcessUndockWindow(ctx, window, clear_settings_refs);
+ if (!clear_settings_refs)
+ IM_ASSERT(window->DockId == backup_dock_id);
+ }
+ }
+}
+
+// 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_at_opposite_dir)
+{
+ 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)
+ {
+ IM_ASSERT(node != NULL);
+ return 0;
+ }
+
+ IM_ASSERT(!node->IsSplitNode()); // Assert if 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_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_at_opposite_dir)
+ *out_id_at_opposite_dir = id_at_opposite_dir;
+ return id_at_dir;
+}
+
+static ImGuiDockNode* DockBuilderCopyNodeRec(ImGuiDockNode* src_node, ImGuiID dst_node_id_if_known, ImVector<ImGuiID>* out_node_remap_pairs)
+{
+ ImGuiContext* ctx = GImGui;
+ 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);
+
+ 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(src_node->ChildNodes[child_n], 0, out_node_remap_pairs);
+ dst_node->ChildNodes[child_n]->ParentNode = dst_node;
+ }
+
+ 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;
+}
+
+void ImGui::DockBuilderCopyNode(ImGuiID src_node_id, ImGuiID dst_node_id, ImVector<ImGuiID>* 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);
+
+ DockBuilderRemoveNode(dst_node_id);
+
+ ImGuiDockNode* src_node = DockContextFindNodeByID(ctx, src_node_id);
+ IM_ASSERT(src_node != NULL);
+
+ out_node_remap_pairs->clear();
+ DockBuilderCopyNodeRec(src_node, dst_node_id, out_node_remap_pairs);
+
+ IM_ASSERT((out_node_remap_pairs->Size % 2) == 0);
+}
+
+void ImGui::DockBuilderCopyWindowSettings(const char* src_name, const char* dst_name)
+{
+ 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))
+ {
+ ImVec2ih window_pos_2ih = ImVec2ih(src_window->Pos);
+ if (src_window->ViewportId != 0 && src_window->ViewportId != IMGUI_VIEWPORT_DEFAULT_ID)
+ {
+ dst_settings->ViewportPos = window_pos_2ih;
+ dst_settings->ViewportId = src_window->ViewportId;
+ dst_settings->Pos = ImVec2ih(0, 0);
+ }
+ else
+ {
+ dst_settings->Pos = window_pos_2ih;
+ }
+ dst_settings->Size = ImVec2ih(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(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector<const char*>* 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 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<ImGuiID> 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)
+ ImVector<ImGuiID> 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 = ImHashStr(src_window_name);
+ 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_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);
+ }
+ }
+
+ // 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* node = DockBuilderGetNode(src_dock_id);
+ for (int window_n = 0; window_n < node->Windows.Size; window_n++)
+ {
+ ImGuiWindow* window = node->Windows[window_n];
+ if (src_windows.contains(window->ID))
+ continue;
+
+ // Docked windows gets redocked into the new node hierarchy.
+ IMGUI_DEBUG_LOG_DOCKING("Remap window '%s' %08X -> %08X\n", window->Name, src_dock_id, dst_dock_id);
+ DockBuilderDockWindow(window->Name, dst_dock_id);
+ }
+ }
+}
+
+// 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;
+ //DockContextRebuild(ctx);
+ DockContextBuildAddWindowsToNodes(ctx, root_id);
+}
+
+//-----------------------------------------------------------------------------
+// Docking: Begin/End Support Functions (called from Begin/End)
+//-----------------------------------------------------------------------------
+// - GetWindowAlwaysWantOwnTabBar()
+// - DockContextBindNodeToWindow()
+// - BeginDocked()
+// - BeginDockableDragDropSource()
+// - BeginDockableDragDropTarget()
+//-----------------------------------------------------------------------------
+
+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->IsFallbackWindow) // We don't support AlwaysTabBar on the fallback/implicit window to avoid unused dock-node overhead/noise
+ return true;
+ 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->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, node);
+ }
+
+ // 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;
+}
+
+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)
+ {
+ 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.Flags & ImGuiNextWindowDataFlags_HasPos) && (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) && g.NextWindowData.PosUndock;
+ if (want_undock)
+ {
+ DockContextProcessUndockWindow(ctx, window);
+ return;
+ }
+ }
+
+ // Bind to our dock node
+ ImGuiDockNode* node = window->DockNode;
+ if (node != NULL)
+ IM_ASSERT(window->DockId == node->ID);
+ if (window->DockId != 0 && node == NULL)
+ {
+ node = DockContextBindNodeToWindow(ctx, window);
+ if (node == NULL)
+ return;
+ }
+
+#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.
+ if (node->LastFrameAlive < g.FrameCount)
+ {
+ // 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;
+ 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)
+ if (node->HostWindow == NULL)
+ {
+ if (node->State == ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing)
+ window->DockIsActive = true;
+ if (node->Windows.Size > 1)
+ DockNodeHideWindowDuringHostWindowCreation(window);
+ 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);
+ node->State = ImGuiDockNodeState_HostWindowVisible;
+
+ // Undock if we are submitted earlier than the host window
+ if (!(node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly) && window->BeginOrderWithinContext < node->HostWindow->BeginOrderWithinContext)
+ {
+ DockContextProcessUndockWindow(ctx, window);
+ return;
+ }
+
+ // Position/Size window
+ SetNextWindowPos(node->Pos);
+ SetNextWindowSize(node->Size);
+ g.NextWindowData.PosUndock = false; // Cancel implicit undocking of SetNextWindowPos()
+ window->DockIsActive = true;
+ window->DockNodeIsVisible = true;
+ window->DockTabIsVisible = false;
+ if (node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly)
+ return;
+
+ // When the window is selected we mark it as visible.
+ if (node->VisibleWindow == window)
+ window->DockTabIsVisible = true;
+
+ // Update window flag
+ IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) == 0);
+ window->Flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_NoResize;
+ 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!
+
+ // 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 && window->WasActive)
+ window->DockOrder = (short)DockNodeGetTabOrder(window);
+
+ if ((node->WantCloseAll || node->WantCloseTabId == window->TabId) && 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::BeginDockableDragDropSource(ImGuiWindow* window)
+{
+ ImGuiContext& g = *GImGui;
+ IM_ASSERT(g.ActiveId == window->MoveId);
+ IM_ASSERT(g.MovingWindow == window);
+ IM_ASSERT(g.CurrentWindow == window);
+
+ g.LastItemData.ID = 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); // 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));
+ 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]]);
+ }
+}
+
+void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window)
+{
+ ImGuiContext* ctx = GImGui;
+ ImGuiContext& g = *ctx;
+
+ //IM_ASSERT(window->RootWindowDockTree == window); // May also be a DockSpace
+ 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;
+
+ // 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))
+ {
+ // Select target node
+ // (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.
+ // 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 (window->DockNode)
+ node = window->DockNode;
+ else
+ 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()));
+ 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 && (node != NULL || dock_into_floating_window))
+ {
+ ImGuiDockPreviewData split_inner;
+ ImGuiDockPreviewData split_outer;
+ ImGuiDockPreviewData* split_data = &split_inner;
+ if (node && (node->ParentNode || node->IsCentralNode()))
+ if (ImGuiDockNode* root_node = DockNodeGetRootNode(node))
+ {
+ DockNodePreviewDockSetup(window, root_node, payload_window, &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);
+ 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, node, payload_window, &split_inner);
+ DockNodePreviewDockRender(window, node, payload_window, &split_outer);
+
+ // Queue docking request
+ if (split_data->IsDropAllowed && payload->IsDelivery())
+ DockContextQueueDock(ctx, window, split_data->SplitNode, payload_window, split_data->SplitDir, split_data->SplitRatio, split_data == &split_outer);
+ }
+ }
+ EndDragDropTarget();
+}
+
+//-----------------------------------------------------------------------------
+// Docking: Settings
+//-----------------------------------------------------------------------------
+// - DockSettingsRenameNodeReferences()
+// - DockSettingsRemoveNodeReferences()
+// - DockSettingsFindNodeSettings()
+// - DockSettingsHandler_ApplyAll()
+// - DockSettingsHandler_ReadOpen()
+// - DockSettingsHandler_ReadLine()
+// - DockSettingsHandler_DockNodeToSettings()
+// - DockSettingsHandler_WriteAll()
+//-----------------------------------------------------------------------------
+
+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];
+ if (window->DockId == old_node_id && window->DockNode == NULL)
+ window->DockId = new_node_id;
+ }
+ //// FIXME-OPT: We could remove this loop by storing the index in the map
+ for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
+ if (settings->DockId == old_node_id)
+ settings->DockId = new_node_id;
+}
+
+// Remove references stored in ImGuiWindowSettings to the given ImGuiDockNodeSettings
+static void ImGui::DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count)
+{
+ ImGuiContext& g = *GImGui;
+ int found = 0;
+ //// FIXME-OPT: We could remove this loop by storing the index in the map
+ for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
+ for (int node_n = 0; node_n < node_ids_count; node_n++)
+ if (settings->DockId == node_ids[node_n])
+ {
+ settings->DockId = 0;
+ settings->DockOrder = -1;
+ if (++found < node_ids_count)
+ break;
+ return;
+ }
+}
+
+static ImGuiDockNodeSettings* ImGui::DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID id)
+{
+ // FIXME-OPT
+ 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->NodesSettings.clear();
+ DockContextClearNodes(ctx, 0, true);
+}
+
+// Recreate nodes based on settings data
+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->NodesSettings.Data, dc->NodesSettings.Size);
+ DockContextBuildAddWindowsToNodes(ctx, 0);
+}
+
+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* ctx, ImGuiSettingsHandler*, void*, const char* line)
+{
+ char c = 0;
+ 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 "
+ // 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.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, " 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, " 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; }
+ 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.SelectedTabId,&r) == 1) { line += r; }
+ if (node.ParentNodeId != 0)
+ if (ImGuiDockNodeSettings* parent_settings = DockSettingsFindNodeSettings(ctx, node.ParentNodeId))
+ node.Depth = parent_settings->Depth + 1;
+ ctx->DockContext.NodesSettings.push_back(node);
+}
+
+static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDockNode* node, int depth)
+{
+ 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.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_);
+ node_settings.Pos = ImVec2ih(node->Pos);
+ node_settings.Size = ImVec2ih(node->Size);
+ node_settings.SizeRef = ImVec2ih(node->SizeRef);
+ dc->NodesSettings.push_back(node_settings);
+ if (node->ChildNodes[0])
+ DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[0], depth + 1);
+ if (node->ChildNodes[1])
+ DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[1], depth + 1);
+}
+
+static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
+{
+ ImGuiContext& g = *ctx;
+ 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->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->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->NodesSettings.Size; node_n++)
+ {
+ const int line_start_pos = buf->size(); (void)line_start_pos;
+ 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)
+ {
+ 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)
+ buf->appendf(" NoResize=1");
+ if (node_settings->Flags & ImGuiDockNodeFlags_CentralNode)
+ buf->appendf(" CentralNode=1");
+ 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(" Selected=0x%08X", node_settings->SelectedTabId);
+
+#if IMGUI_DEBUG_INI_SETTINGS
+ // [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
+ 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 (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' ", settings->GetName());
+ }
+ }
+#endif
+ buf->appendf("\n");
+ }
+ buf->appendf("\n");
+}
//-----------------------------------------------------------------------------
@@ -11975,14 +17476,14 @@ static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport* viewport, ImGuiPlatf
if (HIMC himc = ::ImmGetContext(hwnd))
{
COMPOSITIONFORM composition_form = {};
- composition_form.ptCurrentPos.x = (LONG)data->InputPos.x;
- composition_form.ptCurrentPos.y = (LONG)data->InputPos.y;
+ composition_form.ptCurrentPos.x = (LONG)(data->InputPos.x - viewport->Pos.x);
+ composition_form.ptCurrentPos.y = (LONG)(data->InputPos.y - viewport->Pos.y);
composition_form.dwStyle = CFS_FORCE_POSITION;
::ImmSetCompositionWindow(himc, &composition_form);
CANDIDATEFORM candidate_form = {};
candidate_form.dwStyle = CFS_CANDIDATEPOS;
- candidate_form.ptCurrentPos.x = (LONG)data->InputPos.x;
- candidate_form.ptCurrentPos.y = (LONG)data->InputPos.y;
+ candidate_form.ptCurrentPos.x = (LONG)(data->InputPos.x - viewport->Pos.x);
+ candidate_form.ptCurrentPos.y = (LONG)(data->InputPos.y - viewport->Pos.y);
::ImmSetCandidateWindow(himc, &candidate_form);
::ImmReleaseContext(hwnd, himc);
}
@@ -12002,6 +17503,7 @@ static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport*, ImGuiPlatformImeDat
// - MetricsHelpMarker() [Internal]
// - ShowMetricsWindow()
// - DebugNodeColumns() [Internal]
+// - DebugNodeDockNode() [Internal]
// - DebugNodeDrawList() [Internal]
// - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal]
// - DebugNodeStorage() [Internal]
@@ -12022,13 +17524,15 @@ void ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP*
ImVec2 scale = bb.GetSize() / viewport->Size;
ImVec2 off = bb.Min - viewport->Pos * scale;
- float alpha_mul = 1.0f;
- window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f));
+ 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++)
{
ImGuiWindow* thumb_window = g.Windows[i];
if (!thumb_window->WasActive || (thumb_window->Flags & ImGuiWindowFlags_ChildWindow))
continue;
+ if (thumb_window->Viewport != viewport)
+ continue;
ImRect thumb_r = thumb_window->Rect();
ImRect title_r = thumb_window->TitleBarRect();
@@ -12066,6 +17570,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)
{
@@ -12121,19 +17632,20 @@ void ImGui::ShowMetricsWindow(bool* p_open)
{
static ImRect GetTableRect(ImGuiTable* table, int rect_type, int n)
{
+ ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); // Always using last submitted instance
if (rect_type == TRT_OuterRect) { return table->OuterRect; }
else if (rect_type == TRT_InnerRect) { return table->InnerRect; }
else if (rect_type == TRT_WorkRect) { return table->WorkRect; }
else if (rect_type == TRT_HostClipRect) { return table->HostClipRect; }
else if (rect_type == TRT_InnerClipRect) { return table->InnerClipRect; }
else if (rect_type == TRT_BackgroundClipRect) { return table->BgClipRect; }
- else if (rect_type == TRT_ColumnsRect) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->MinX, table->InnerClipRect.Min.y, c->MaxX, table->InnerClipRect.Min.y + table->LastOuterHeight); }
+ else if (rect_type == TRT_ColumnsRect) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->MinX, table->InnerClipRect.Min.y, c->MaxX, table->InnerClipRect.Min.y + table_instance->LastOuterHeight); }
else if (rect_type == TRT_ColumnsWorkRect) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->WorkRect.Min.y, c->WorkMaxX, table->WorkRect.Max.y); }
else if (rect_type == TRT_ColumnsClipRect) { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; }
- else if (rect_type == TRT_ColumnsContentHeadersUsed){ ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersUsed, table->InnerClipRect.Min.y + table->LastFirstRowHeight); } // Note: y1/y2 not always accurate
- else if (rect_type == TRT_ColumnsContentHeadersIdeal){ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersIdeal, table->InnerClipRect.Min.y + table->LastFirstRowHeight); }
- else if (rect_type == TRT_ColumnsContentFrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXFrozen, table->InnerClipRect.Min.y + table->LastFirstRowHeight); }
- else if (rect_type == TRT_ColumnsContentUnfrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y + table->LastFirstRowHeight, c->ContentMaxXUnfrozen, table->InnerClipRect.Max.y); }
+ else if (rect_type == TRT_ColumnsContentHeadersUsed){ ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersUsed, table->InnerClipRect.Min.y + table_instance->LastFirstRowHeight); } // Note: y1/y2 not always accurate
+ else if (rect_type == TRT_ColumnsContentHeadersIdeal){ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersIdeal, table->InnerClipRect.Min.y + table_instance->LastFirstRowHeight); }
+ else if (rect_type == TRT_ColumnsContentFrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXFrozen, table->InnerClipRect.Min.y + table_instance->LastFirstRowHeight); }
+ else if (rect_type == TRT_ColumnsContentUnfrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y + table_instance->LastFirstRowHeight, c->ContentMaxXUnfrozen, table->InnerClipRect.Max.y); }
IM_ASSERT(0);
return ImRect();
}
@@ -12266,9 +17778,15 @@ void ImGui::ShowMetricsWindow(bool* p_open)
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++)
- DebugNodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList");
+ {
+ if (!viewport_has_drawlist)
+ 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();
}
@@ -12279,6 +17797,36 @@ void ImGui::ShowMetricsWindow(bool* p_open)
Indent(GetTreeNodeToLabelSpacing());
RenderViewportsThumbnails();
Unindent(GetTreeNodeToLabelSpacing());
+
+ bool open = TreeNode("Monitors", "Monitors (%d)", g.PlatformIO.Monitors.Size);
+ 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)
+ {
+ for (int i = 0; i < g.PlatformIO.Monitors.Size; i++)
+ {
+ const ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[i];
+ 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.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);
+ }
+ TreePop();
+ }
+
+ 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<ImGuiViewportP*> 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();
@@ -12331,6 +17879,17 @@ void ImGui::ShowMetricsWindow(bool* p_open)
#ifdef IMGUI_HAS_DOCK
if (TreeNode("Docking"))
{
+ static bool root_nodes_only = true;
+ ImGuiDockContext* dc = &g.DockContext;
+ Checkbox("List root nodes", &root_nodes_only);
+ Checkbox("Ctrl shows window dock info", &cfg->ShowDockingNodes);
+ if (SmallButton("Clear nodes")) { DockContextClearNodes(&g, 0, true); }
+ SameLine();
+ if (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 (!root_nodes_only || node->IsRootNode())
+ DebugNodeDockNode(node, "Node");
TreePop();
}
#endif // #ifdef IMGUI_HAS_DOCK
@@ -12373,6 +17932,29 @@ void ImGui::ShowMetricsWindow(bool* p_open)
}
#ifdef IMGUI_HAS_DOCK
+ if (TreeNode("SettingsDocking", "Settings packed data: Docking"))
+ {
+ ImGuiDockContext* dc = &g.DockContext;
+ Text("In SettingsWindows:");
+ for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
+ if (settings->DockId != 0)
+ 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];
+ 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->GetName();
+ }
+ 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();
+ }
#endif // #ifdef IMGUI_HAS_DOCK
if (TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size()))
@@ -12389,9 +17971,11 @@ 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");
+ Text("MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)", g.MouseViewport->ID, g.IO.MouseHoveredViewport, g.MouseLastHoveredViewport ? g.MouseLastHoveredViewport->ID : 0);
Unindent();
Text("ITEMS");
@@ -12400,7 +17984,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
int active_id_using_key_input_count = 0;
- for (int n = 0; n < ImGuiKey_NamedKey_COUNT; n++)
+ for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++)
active_id_using_key_input_count += g.ActiveIdUsingKeyInputMask[n] ? 1 : 0;
Text("ActiveIdUsing: Wheel: %d, NavDirMask: %X, NavInputMask: %X, KeyInputMask: %d key(s)", g.ActiveIdUsingMouseWheel, g.ActiveIdUsingNavDirMask, g.ActiveIdUsingNavInputMask, active_id_using_key_input_count);
Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame
@@ -12477,8 +18061,21 @@ void ImGui::ShowMetricsWindow(bool* p_open)
#ifdef IMGUI_HAS_DOCK
// Overlay: Display Docking info
- if (show_docking_nodes && g.IO.KeyCtrl)
+ if (cfg->ShowDockingNodes && 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(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
@@ -12515,8 +18112,92 @@ 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)
+ BeginDisabled();
+ 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);
+ CheckboxFlags("NoDockingOverEmpty", p_flags, ImGuiDockNodeFlags_NoDockingOverEmpty);
+ if (!enabled)
+ EndDisabled();
+ PopStyleVar();
+ PopID();
+}
+
+// [DEBUG] Display contents of ImDockNode
+void ImGui::DebugNodeDockNode(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 = 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 = 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 && IsItemHovered())
+ 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);
+ IM_ASSERT(node->ChildNodes[1] == NULL || node->ChildNodes[1]->ParentNode == node);
+ 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);
+ 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",
+ node->IsDockSpace() ? " IsDockSpace" : "",
+ node->IsCentralNode() ? " IsCentralNode" : "",
+ is_alive ? " IsAlive" : "", is_active ? " IsActive" : "",
+ 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))
+ {
+ 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)
+ DebugNodeDockNode(node->ParentNode, "ParentNode");
+ if (node->ChildNodes[0])
+ DebugNodeDockNode(node->ChildNodes[0], "Child[0]");
+ if (node->ChildNodes[1])
+ DebugNodeDockNode(node->ChildNodes[1], "Child[1]");
+ if (node->TabBar)
+ DebugNodeTabBar(node->TabBar, "TabBar");
+ TreePop();
+ }
+}
+
// [DEBUG] Display contents of ImDrawList
-void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, const char* label)
+// Note that both 'window' and 'viewport' may be NULL here. Viewport is generally null of destroyed popups which previously owned a viewport.
+void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label)
{
ImGuiContext& g = *GImGui;
ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
@@ -12533,7 +18214,7 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list,
return;
}
- ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list
+ ImDrawList* fg_draw_list = viewport ? GetForegroundDrawList(viewport) : NULL; // Render additional visuals into the top-most draw list
if (window && IsItemHovered() && fg_draw_list)
fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
if (!node_open)
@@ -12762,7 +18443,7 @@ void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label)
{
ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
p += ImFormatString(p, buf_end - p, "%s'%s'",
- tab_n > 0 ? ", " : "", (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???");
+ 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)); }
@@ -12784,7 +18465,7 @@ void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label)
if (SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } SameLine(0, 2);
if (SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } SameLine();
Text("%02d%c Tab 0x%08X '%s' Offset: %.1f, Width: %.1f/%.1f",
- tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???", tab->Offset, tab->Width, tab->ContentWidth);
+ tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->Window || tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???", tab->Offset, tab->Width, tab->ContentWidth);
PopID();
}
TreePop();
@@ -12794,19 +18475,31 @@ void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label)
void ImGui::DebugNodeViewport(ImGuiViewportP* viewport)
{
SetNextItemOpen(true, ImGuiCond_Once);
- if (TreeNode("viewport0", "Viewport #%d", 0))
+ if (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;
- BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Offset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f",
+ 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);
- BulletText("Flags: 0x%04X =%s%s%s", viewport->Flags,
- (flags & ImGuiViewportFlags_IsPlatformWindow) ? " IsPlatformWindow" : "",
+ 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", viewport->Flags,
+ //(flags & ImGuiViewportFlags_IsPlatformWindow) ? " IsPlatformWindow" : "", // Omitting because it is the standard
(flags & ImGuiViewportFlags_IsPlatformMonitor) ? " IsPlatformMonitor" : "",
- (flags & ImGuiViewportFlags_OwnedByApp) ? " OwnedByApp" : "");
+ (flags & ImGuiViewportFlags_OwnedByApp) ? " OwnedByApp" : "",
+ (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->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList");
+ DebugNodeDrawList(NULL, viewport, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList");
TreePop();
}
}
@@ -12834,12 +18527,13 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
TextDisabled("Note: some memory buffers have been compacted/freed.");
ImGuiWindowFlags flags = window->Flags;
- DebugNodeDrawList(window, window->DrawList, "DrawList");
+ DebugNodeDrawList(window, window->Viewport, window->DrawList, "DrawList");
BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f) Ideal (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y, window->ContentSizeIdeal.x, window->ContentSizeIdeal.y);
BulletText("Flags: 0x%08X (%s%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_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
+ BulletText("WindowClassId: 0x%08X", window->WindowClass.ClassId);
BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y, window->ScrollbarX ? "X" : "", window->ScrollbarY ? "Y" : "");
BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);
@@ -12856,7 +18550,15 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
GetForegroundDrawList(window)->AddRect(r.Min + window->Pos, r.Max + window->Pos, IM_COL32(255, 255, 0, 255));
}
BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
+
+ 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);
+ BulletText("ViewportMonitor: %d", window->Viewport ? window->Viewport->PlatformMonitor : -1);
+ 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->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))
@@ -13000,27 +18702,44 @@ void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* dat
ImGuiStackLevelInfo* info = &tool->Results[tool->StackLevel];
IM_ASSERT(info->ID == id && info->QueryFrameCount > 0);
- int data_len;
switch (data_type)
{
case ImGuiDataType_S32:
ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "%d", (int)(intptr_t)data_id);
break;
case ImGuiDataType_String:
- data_len = data_id_end ? (int)((const char*)data_id_end - (const char*)data_id) : (int)strlen((const char*)data_id);
- ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "\"%.*s\"", data_len, (const char*)data_id);
+ ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "%.*s", data_id_end ? (int)((const char*)data_id_end - (const char*)data_id) : (int)strlen((const char*)data_id), (const char*)data_id);
break;
case ImGuiDataType_Pointer:
ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "(void*)0x%p", data_id);
break;
case ImGuiDataType_ID:
- if (info->Desc[0] == 0) // PushOverrideID() is often used to avoid hashing twice, which would lead to 2 calls to DebugHookIdInfo(). We prioritize the first one.
- ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "0x%08X [override]", id);
+ if (info->Desc[0] != 0) // PushOverrideID() is often used to avoid hashing twice, which would lead to 2 calls to DebugHookIdInfo(). We prioritize the first one.
+ return;
+ ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "0x%08X [override]", id);
break;
default:
IM_ASSERT(0);
}
info->QuerySuccess = true;
+ info->DataType = data_type;
+}
+
+static int StackToolFormatLevelInfo(ImGuiStackTool* tool, int n, bool format_for_ui, char* buf, size_t buf_size)
+{
+ ImGuiStackLevelInfo* info = &tool->Results[n];
+ ImGuiWindow* window = (info->Desc[0] == 0 && n == 0) ? ImGui::FindWindowByID(info->ID) : NULL;
+ if (window) // Source: window name (because the root ID don't call GetID() and so doesn't get hooked)
+ return ImFormatString(buf, buf_size, format_for_ui ? "\"%s\" [window]" : "%s", window->Name);
+ if (info->QuerySuccess) // Source: GetID() hooks (prioritize over ItemInfo() because we frequently use patterns like: PushID(str), Button("") where they both have same id)
+ return ImFormatString(buf, buf_size, (format_for_ui && info->DataType == ImGuiDataType_String) ? "\"%s\"" : "%s", info->Desc);
+ if (tool->StackLevel < tool->Results.Size) // Only start using fallback below when all queries are done, so during queries we don't flickering ??? markers.
+ return (*buf = 0);
+#ifdef IMGUI_ENABLE_TEST_ENGINE
+ if (const char* label = ImGuiTestEngine_FindItemDebugLabel(GImGui, info->ID)) // Source: ImGuiTestEngine's ItemInfo()
+ return ImFormatString(buf, buf_size, format_for_ui ? "??? \"%s\"" : "%s", label);
+#endif
+ return ImFormatString(buf, buf_size, "???");
}
// Stack Tool: Display UI
@@ -13036,6 +18755,7 @@ void ImGui::ShowStackToolWindow(bool* p_open)
}
// Display hovered/active status
+ ImGuiStackTool* tool = &g.DebugStackTool;
const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
const ImGuiID active_id = g.ActiveId;
#ifdef IMGUI_ENABLE_TEST_ENGINE
@@ -13046,8 +18766,33 @@ void ImGui::ShowStackToolWindow(bool* p_open)
SameLine();
MetricsHelpMarker("Hover an item with the mouse to display elements of the ID Stack leading to the item's final ID.\nEach level of the stack correspond to a PushID() call.\nAll levels of the stack are hashed together to make the final ID of a widget (ID displayed at the bottom level of the stack).\nRead FAQ entry about the ID stack for details.");
+ // CTRL+C to copy path
+ const float time_since_copy = (float)g.Time - tool->CopyToClipboardLastTime;
+ Checkbox("Ctrl+C: copy path to clipboard", &tool->CopyToClipboardOnCtrlC);
+ SameLine();
+ TextColored((time_since_copy >= 0.0f && time_since_copy < 0.75f && ImFmod(time_since_copy, 0.25f) < 0.25f * 0.5f) ? ImVec4(1.f, 1.f, 0.3f, 1.f) : ImVec4(), "*COPIED*");
+ if (tool->CopyToClipboardOnCtrlC && IsKeyDown(ImGuiKey_ModCtrl) && IsKeyPressed(ImGuiKey_C))
+ {
+ tool->CopyToClipboardLastTime = (float)g.Time;
+ char* p = g.TempBuffer;
+ char* p_end = p + IM_ARRAYSIZE(g.TempBuffer);
+ for (int stack_n = 0; stack_n < tool->Results.Size && p + 3 < p_end; stack_n++)
+ {
+ *p++ = '/';
+ char level_desc[256];
+ StackToolFormatLevelInfo(tool, stack_n, false, level_desc, IM_ARRAYSIZE(level_desc));
+ for (int n = 0; level_desc[n] && p + 2 < p_end; n++)
+ {
+ if (level_desc[n] == '/')
+ *p++ = '\\';
+ *p++ = level_desc[n];
+ }
+ }
+ *p = '\0';
+ SetClipboardText(g.TempBuffer);
+ }
+
// Display decorated stack
- ImGuiStackTool* tool = &g.DebugStackTool;
tool->LastActiveFrame = g.FrameCount;
if (tool->Results.Size > 0 && BeginTable("##table", 3, ImGuiTableFlags_Borders))
{
@@ -13061,23 +18806,9 @@ void ImGui::ShowStackToolWindow(bool* p_open)
ImGuiStackLevelInfo* info = &tool->Results[n];
TableNextColumn();
Text("0x%08X", (n > 0) ? tool->Results[n - 1].ID : 0);
-
TableNextColumn();
- ImGuiWindow* window = (info->Desc[0] == 0 && n == 0) ? FindWindowByID(info->ID) : NULL;
- if (window) // Source: window name (because the root ID don't call GetID() and so doesn't get hooked)
- Text("\"%s\" [window]", window->Name);
- else if (info->QuerySuccess) // Source: GetID() hooks (prioritize over ItemInfo() because we frequently use patterns like: PushID(str), Button("") where they both have same id)
- TextUnformatted(info->Desc);
- else if (tool->StackLevel >= tool->Results.Size) // Only start using fallback below when all queries are done, so during queries we don't flickering ??? markers.
- {
-#ifdef IMGUI_ENABLE_TEST_ENGINE
- if (const char* label = ImGuiTestEngine_FindItemDebugLabel(&g, info->ID)) // Source: ImGuiTestEngine's ItemInfo()
- Text("??? \"%s\"", label);
- else
-#endif
- TextUnformatted("???");
- }
-
+ StackToolFormatLevelInfo(tool, n, true, g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer));
+ TextUnformatted(g.TempBuffer);
TableNextColumn();
Text("0x%08X", info->ID);
if (n == tool->Results.Size - 1)
@@ -13093,7 +18824,7 @@ void ImGui::ShowStackToolWindow(bool* p_open)
void ImGui::ShowMetricsWindow(bool*) {}
void ImGui::ShowFontAtlas(ImFontAtlas*) {}
void ImGui::DebugNodeColumns(ImGuiOldColumns*) {}
-void ImGui::DebugNodeDrawList(ImGuiWindow*, const ImDrawList*, const char*) {}
+void ImGui::DebugNodeDrawList(ImGuiWindow*, ImGuiViewportP*, const ImDrawList*, const char*) {}
void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList*, const ImDrawList*, const ImDrawCmd*, bool, bool) {}
void ImGui::DebugNodeFont(ImFont*) {}
void ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {}
diff --git a/3rdparty/imgui/source/imgui.h b/3rdparty/imgui/source/imgui.h
index 871b75f..262863e 100644
--- a/3rdparty/imgui/source/imgui.h
+++ b/3rdparty/imgui/source/imgui.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.87
+// dear imgui, v1.88 WIP
// (headers)
// Help:
@@ -30,12 +30,12 @@ Index of this file:
// [SECTION] Helpers: Memory allocations macros, ImVector<>
// [SECTION] ImGuiStyle
// [SECTION] ImGuiIO
-// [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload, ImGuiTableSortSpecs, ImGuiTableColumnSortSpecs)
+// [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiWindowClass, ImGuiPayload, ImGuiTableSortSpecs, ImGuiTableColumnSortSpecs)
// [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, ImColor)
// [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawFlags, ImDrawListFlags, ImDrawList, ImDrawData)
// [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont)
// [SECTION] Viewports (ImGuiViewportFlags, ImGuiViewport)
-// [SECTION] Platform Dependent Interfaces (ImGuiPlatformImeData)
+// [SECTION] Platform Dependent Interfaces (ImGuiPlatformIO, ImGuiPlatformMonitor, ImGuiPlatformImeData)
// [SECTION] Obsolete functions and types
*/
@@ -64,10 +64,12 @@ 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 "1.88 WIP"
+#define IMGUI_VERSION_NUM 18718
#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
+#define IMGUI_HAS_DOCK // 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 backends files (imgui_impl_xxx.h)
@@ -152,6 +154,8 @@ struct ImGuiKeyData; // Storage for ImGuiIO and IsKeyDown(), IsKe
struct ImGuiListClipper; // Helper to manually clip large list of items
struct ImGuiOnceUponAFrame; // Helper for running a block of code not more than once a frame
struct ImGuiPayload; // User data payload for drag and drop operations
+struct ImGuiPlatformIO; // Multi-viewport support: interface for Platform/Renderer backends + 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 ImGuiPlatformImeData; // Platform IME data for io.SetPlatformImeDataFn() function.
struct ImGuiSizeCallbackData; // Callback data when using SetNextWindowSizeConstraints() (rare/advanced use)
struct ImGuiStorage; // Helper for key->value storage
@@ -160,11 +164,12 @@ struct ImGuiTableSortSpecs; // Sorting specifications for a table (often
struct ImGuiTableColumnSortSpecs; // Sorting specification for one column of a table
struct ImGuiTextBuffer; // Helper to hold and append into a text buffer (~string builder)
struct ImGuiTextFilter; // Helper to parse and apply text filters (e.g. "aaaaa[,bbbbb][,ccccc]")
-struct ImGuiViewport; // A Platform Window (always only one in 'master' branch), in the future may represent Platform Monitor
+struct ImGuiViewport; // A Platform Window (always 1 unless multi-viewport are enabled. One per platform window to output to). In the future may represent Platform Monitor
+struct ImGuiWindowClass; // Window class (rare/advanced uses: provide hints to the platform backend via altered viewport flags and parent/child info)
// Enums/Flags (declared as int for compatibility with old C++, to allow using as flags without overhead, and to not pollute the top of this file)
// - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists!
-// In Visual Studio IDE: CTRL+comma ("Edit.NavigateTo") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
+// In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
// With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments.
typedef int ImGuiCol; // -> enum ImGuiCol_ // Enum: A color identifier for styling
typedef int ImGuiCond; // -> enum ImGuiCond_ // Enum: A condition for many Set*() functions
@@ -185,11 +190,12 @@ typedef int ImGuiButtonFlags; // -> enum ImGuiButtonFlags_ // Flags: f
typedef int ImGuiColorEditFlags; // -> enum ImGuiColorEditFlags_ // Flags: for ColorEdit4(), ColorPicker4() etc.
typedef int ImGuiConfigFlags; // -> enum ImGuiConfigFlags_ // Flags: for io.ConfigFlags
typedef int ImGuiComboFlags; // -> enum ImGuiComboFlags_ // Flags: for BeginCombo()
+typedef int ImGuiDockNodeFlags; // -> enum ImGuiDockNodeFlags_ // Flags: for DockSpace()
typedef int ImGuiDragDropFlags; // -> enum ImGuiDragDropFlags_ // Flags: for BeginDragDropSource(), AcceptDragDropPayload()
typedef int ImGuiFocusedFlags; // -> enum ImGuiFocusedFlags_ // Flags: for IsWindowFocused()
typedef int ImGuiHoveredFlags; // -> enum ImGuiHoveredFlags_ // Flags: for IsItemHovered(), IsWindowHovered() etc.
typedef int ImGuiInputTextFlags; // -> enum ImGuiInputTextFlags_ // Flags: for InputText(), InputTextMultiline()
-typedef int ImGuiKeyModFlags; // -> enum ImGuiKeyModFlags_ // Flags: for io.KeyMods (Ctrl/Shift/Alt/Super)
+typedef int ImGuiModFlags; // -> enum ImGuiModFlags_ // Flags: for io.KeyMods (Ctrl/Shift/Alt/Super)
typedef int ImGuiPopupFlags; // -> enum ImGuiPopupFlags_ // Flags: for OpenPopup*(), BeginPopupContext*(), IsPopupOpen()
typedef int ImGuiSelectableFlags; // -> enum ImGuiSelectableFlags_ // Flags: for Selectable()
typedef int ImGuiSliderFlags; // -> enum ImGuiSliderFlags_ // Flags: for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc.
@@ -249,8 +255,8 @@ IM_MSVC_RUNTIME_CHECKS_OFF
struct ImVec2
{
float x, y;
- ImVec2() { x = y = 0.0f; }
- ImVec2(float _x, float _y) { x = _x; y = _y; }
+ constexpr ImVec2() : x(0.0f), y(0.0f) { }
+ constexpr ImVec2(float _x, float _y) : x(_x), y(_y) { }
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
@@ -261,9 +267,9 @@ struct ImVec2
// ImVec4: 4D vector used to store clipping rectangles, colors etc. [Compile-time configurable type]
struct ImVec4
{
- float x, y, z, w;
- ImVec4() { x = y = z = w = 0.0f; }
- ImVec4(float _x, float _y, float _z, float _w) { x = _x; y = _y; z = _z; w = _w; }
+ float x, y, z, w;
+ constexpr ImVec4() : x(0.0f), y(0.0f), z(0.0f), w(0.0f) { }
+ constexpr ImVec4(float _x, float _y, float _z, float _w) : x(_x), y(_y), z(_z), w(_w) { }
#ifdef IM_VEC4_CLASS_EXTRA
IM_VEC4_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec4.
#endif
@@ -344,10 +350,12 @@ 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 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.
// Window manipulation
// - Prefer using SetNextXXX functions (before Begin) rather that SetXXX functions (after Begin).
@@ -358,6 +366,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 / top-most. call before Begin()
IMGUI_API void SetNextWindowBgAlpha(float alpha); // set next window background color alpha. helper to easily override the Alpha component of ImGuiCol_WindowBg/ChildBg/PopupBg. you may also use ImGuiWindowFlags_NoBackground.
+ 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().
@@ -583,7 +592,7 @@ namespace ImGui
IMGUI_API bool ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0);
IMGUI_API bool ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags = 0);
IMGUI_API bool ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags = 0, const float* ref_col = NULL);
- IMGUI_API bool ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0, 0)); // display a color square/button, hover for details, return true when pressed.
+ IMGUI_API bool ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // display a color square/button, hover for details, return true when pressed.
IMGUI_API void SetColorEditOptions(ImGuiColorEditFlags flags); // initialize current options (generally on application startup) if you want to select a default format, picker type, etc. User will be able to change many settings, unless you pass the _NoOptions flag to your calls.
// Widgets: Trees
@@ -704,11 +713,10 @@ namespace ImGui
// Tables
// - Full-featured replacement for old Columns API.
- // - See Demo->Tables for demo code.
- // - See top of imgui_tables.cpp for general commentary.
+ // - See Demo->Tables for demo code. See top of imgui_tables.cpp for general commentary.
// - See ImGuiTableFlags_ and ImGuiTableColumnFlags_ enums for a description of available flags.
// The typical call flow is:
- // - 1. Call BeginTable().
+ // - 1. Call BeginTable(), early out if returning false.
// - 2. Optionally call TableSetupColumn() to submit column name/flags/defaults.
// - 3. Optionally call TableSetupScrollFreeze() to request scroll freezing of columns/rows.
// - 4. Optionally call TableHeadersRow() to submit a header row. Names are pulled from TableSetupColumn() data.
@@ -727,10 +735,10 @@ namespace ImGui
// --------------------------------------------------------------------------------------------------------
// - 5. Call EndTable()
IMGUI_API bool BeginTable(const char* str_id, int column, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0.0f, 0.0f), float inner_width = 0.0f);
- IMGUI_API void EndTable(); // only call EndTable() if BeginTable() returns true!
+ IMGUI_API void EndTable(); // only call EndTable() if BeginTable() returns true!
IMGUI_API void TableNextRow(ImGuiTableRowFlags row_flags = 0, float min_row_height = 0.0f); // append into the first cell of a new row.
- IMGUI_API bool TableNextColumn(); // append into the next column (or first column of next row if currently in last column). Return true when column is visible.
- IMGUI_API bool TableSetColumnIndex(int column_n); // append into the specified column. Return true when column is visible.
+ IMGUI_API bool TableNextColumn(); // append into the next column (or first column of next row if currently in last column). Return true when column is visible.
+ IMGUI_API bool TableSetColumnIndex(int column_n); // append into the specified column. Return true when column is visible.
// Tables: Headers & Columns declaration
// - Use TableSetupColumn() to specify label, resizing policy, default width/weight, id, various other flags etc.
@@ -741,20 +749,17 @@ namespace ImGui
// some advanced use cases (e.g. adding custom widgets in header row).
// - Use TableSetupScrollFreeze() to lock columns/rows so they stay visible when scrolled.
IMGUI_API void TableSetupColumn(const char* label, ImGuiTableColumnFlags flags = 0, float init_width_or_weight = 0.0f, ImGuiID user_id = 0);
- IMGUI_API void TableSetupScrollFreeze(int cols, int rows); // lock columns/rows so they stay visible when scrolled.
- IMGUI_API void TableHeadersRow(); // submit all headers cells based on data provided to TableSetupColumn() + submit context menu
- IMGUI_API void TableHeader(const char* label); // submit one header cell manually (rarely used)
+ IMGUI_API void TableSetupScrollFreeze(int cols, int rows); // lock columns/rows so they stay visible when scrolled.
+ IMGUI_API void TableHeadersRow(); // submit all headers cells based on data provided to TableSetupColumn() + submit context menu
+ IMGUI_API void TableHeader(const char* label); // submit one header cell manually (rarely used)
- // Tables: Sorting
- // - Call TableGetSortSpecs() to retrieve latest sort specs for the table. NULL when not sorting.
- // - When 'SpecsDirty == true' you should sort your data. It will be true when sorting specs have changed
- // since last call, or the first time. Make sure to set 'SpecsDirty = false' after sorting, else you may
- // wastefully sort your data every frame!
- // - Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable().
- IMGUI_API ImGuiTableSortSpecs* TableGetSortSpecs(); // get latest sort specs for the table (NULL if not sorting).
-
- // Tables: Miscellaneous functions
+ // Tables: Sorting & Miscellaneous functions
+ // - Sorting: call TableGetSortSpecs() to retrieve latest sort specs for the table. NULL when not sorting.
+ // When 'sort_specs->SpecsDirty == true' you should sort your data. It will be true when sorting specs have
+ // changed since last call, or the first time. Make sure to set 'SpecsDirty = false' after sorting,
+ // else you may wastefully sort your data every frame!
// - Functions args 'int column_n' treat the default value of -1 as the same as passing the current column index.
+ IMGUI_API ImGuiTableSortSpecs* TableGetSortSpecs(); // get latest sort specs for the table (NULL if not sorting). Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable().
IMGUI_API int TableGetColumnCount(); // return number of columns (value passed to BeginTable)
IMGUI_API int TableGetColumnIndex(); // return current column index.
IMGUI_API int TableGetRowIndex(); // return current row index.
@@ -775,6 +780,7 @@ namespace ImGui
IMGUI_API int GetColumnsCount();
// Tab Bars, Tabs
+ // 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(); // only call EndTabBar() if BeginTabBar() returns true!
IMGUI_API bool BeginTabItem(const char* label, bool* p_open = NULL, ImGuiTabItemFlags flags = 0); // create a Tab. Returns true if the Tab is selected.
@@ -782,6 +788,26 @@ namespace ImGui
IMGUI_API bool TabItemButton(const char* label, ImGuiTabItemFlags flags = 0); // create a Tab behaving like a button. return true when clicked. cannot be selected in the tab bar.
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 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/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.
+ // 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
+ 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?
+
// Logging/Capture
// - All text output from the interface can be captured into tty/file/clipboard. By default, tree nodes are automatically opened during logging.
IMGUI_API void LogToTTY(int auto_open_depth = -1); // start logging to tty (stdout)
@@ -849,13 +875,17 @@ namespace ImGui
// - In the future we will extend this concept further to also represent Platform Monitor and support a "no main platform window" operation mode.
IMGUI_API ImGuiViewport* GetMainViewport(); // return primary/default viewport. This can never be NULL.
+ // Background/Foreground Draw Lists
+ IMGUI_API ImDrawList* GetBackgroundDrawList(); // get background draw list for the viewport associated to the current window. this draw list will be the first rendering one. Useful to quickly draw shapes/text behind dear imgui contents.
+ IMGUI_API ImDrawList* GetForegroundDrawList(); // get foreground draw list for the viewport associated to the current window. this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents.
+ IMGUI_API ImDrawList* GetBackgroundDrawList(ImGuiViewport* viewport); // get background draw list for the given viewport. this draw list will be the first rendering one. Useful to quickly draw shapes/text behind dear imgui contents.
+ IMGUI_API ImDrawList* GetForegroundDrawList(ImGuiViewport* viewport); // get foreground draw list for the given viewport. this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents.
+
// Miscellaneous Utilities
IMGUI_API bool IsRectVisible(const ImVec2& size); // test if rectangle (of given size, starting from cursor position) is visible / not clipped.
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(); // get global imgui time. incremented by io.DeltaTime every frame.
IMGUI_API int GetFrameCount(); // get global imgui frame count. incremented by 1 every frame.
- IMGUI_API ImDrawList* GetBackgroundDrawList(); // this draw list will be the first rendering one. Useful to quickly draw shapes/text behind dear imgui contents.
- IMGUI_API ImDrawList* GetForegroundDrawList(); // this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents.
IMGUI_API ImDrawListSharedData* GetDrawListSharedData(); // you may use this when creating your own ImDrawList instances.
IMGUI_API const char* GetStyleColorName(ImGuiCol idx); // get a string corresponding to the enum value (for display, saving, etc.).
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)
@@ -933,6 +963,16 @@ namespace ImGui
IMGUI_API void* MemAlloc(size_t size);
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 backend to setup + viewports list.
+ 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().
+ IMGUI_API ImGuiViewport* FindViewportByID(ImGuiID id); // this is a helper for backends.
+ IMGUI_API ImGuiViewport* FindViewportByPlatformHandle(void* platform_handle); // this is a helper for backends. the type platform_handle is decided by the backend (e.g. HWND, MyWindow*, GLFWwindow* etc.)
+
} // namespace ImGui
//-----------------------------------------------------------------------------
@@ -963,6 +1003,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_UnsavedDocument = 1 << 20, // Display a dot next to the title. When used in a tab/docking context, tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.
+ ImGuiWindowFlags_NoDocking = 1 << 21, // Disable docking of this window
+
ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus,
ImGuiWindowFlags_NoDecoration = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse,
ImGuiWindowFlags_NoInputs = ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus,
@@ -973,7 +1015,10 @@ 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_ResizeFromAnySide = 1 << 17, // [Obsolete] --> Set io.ConfigWindowsResizeFromEdges=true and make sure mouse cursors are supported by backend (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors)
};
@@ -1260,7 +1305,7 @@ enum ImGuiFocusedFlags_
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_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_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
};
@@ -1274,16 +1319,32 @@ enum ImGuiHoveredFlags_
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_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_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, // IsItemHovered() only: Return true even if the position is obstructed or overlapped by another window
ImGuiHoveredFlags_AllowWhenDisabled = 1 << 9, // IsItemHovered() only: Return true even if the item is disabled
+ ImGuiHoveredFlags_NoNavOverride = 1 << 10, // Disable using gamepad/keyboard navigation state when active, always query mouse.
ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped,
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 programmatically 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_
{
@@ -1421,7 +1482,7 @@ enum ImGuiKey_
ImGuiKey_GamepadRStickLeft, // [Analog]
ImGuiKey_GamepadRStickRight, // [Analog]
- // Keyboard Modifiers
+ // Keyboard Modifiers (explicitly submitted by backend via AddKeyEvent() calls)
// - This is mirroring the data also written to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper, in a format allowing
// them to be accessed via standard key API, allowing calls such as IsKeyPressed(), IsKeyReleased(), querying duration etc.
// - Code polling every keys (e.g. an interface to detect a key press for input mapping) might want to ignore those
@@ -1429,11 +1490,9 @@ enum ImGuiKey_
// - In theory the value of keyboard modifiers should be roughly equivalent to a logical or of the equivalent left/right keys.
// In practice: it's complicated; mods are often provided from different sources. Keyboard layout, IME, sticky keys and
// backends tend to interfere and break that equivalence. The safer decision is to relay that ambiguity down to the end-user...
- ImGuiKey_ModCtrl,
- ImGuiKey_ModShift,
- ImGuiKey_ModAlt,
- ImGuiKey_ModSuper,
+ ImGuiKey_ModCtrl, ImGuiKey_ModShift, ImGuiKey_ModAlt, ImGuiKey_ModSuper,
+ // End of list
ImGuiKey_COUNT, // No valid ImGuiKey is ever greater than this value
// [Internal] Prior to 1.87 we required user to fill io.KeysDown[512] using their own native index + a io.KeyMap[] array.
@@ -1455,13 +1514,13 @@ enum ImGuiKey_
};
// 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_
+enum ImGuiModFlags_
{
- ImGuiKeyModFlags_None = 0,
- ImGuiKeyModFlags_Ctrl = 1 << 0,
- ImGuiKeyModFlags_Shift = 1 << 1,
- ImGuiKeyModFlags_Alt = 1 << 2,
- ImGuiKeyModFlags_Super = 1 << 3 // Cmd/Super/Windows key
+ ImGuiModFlags_None = 0,
+ ImGuiModFlags_Ctrl = 1 << 0,
+ ImGuiModFlags_Shift = 1 << 1,
+ ImGuiModFlags_Alt = 1 << 2, // Menu
+ ImGuiModFlags_Super = 1 << 3 // Cmd/Super/Windows key
};
// Gamepad/Keyboard navigation
@@ -1508,6 +1567,15 @@ enum ImGuiConfigFlags_
ImGuiConfigFlags_NoMouse = 1 << 4, // Instruct imgui to clear mouse position/buttons in NewFrame(). This allows ignoring the mouse information set by the backend.
ImGuiConfigFlags_NoMouseCursorChange = 1 << 5, // Instruct backend to not alter mouse cursor shape and visibility. Use if the backend 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.
+
+ // [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 ImGuiBackendFlags_PlatformHasViewports + ImGuiBackendFlags_RendererHasViewports set by the respective backends)
+ 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 backend/engine to communicate to code that may be shared between multiple projects. Those flags are not used by core Dear ImGui)
ImGuiConfigFlags_IsSRGB = 1 << 20, // Application is SRGB-aware.
ImGuiConfigFlags_IsTouchScreen = 1 << 21 // Application is using a touch screen instead of a mouse.
@@ -1520,7 +1588,12 @@ enum ImGuiBackendFlags_
ImGuiBackendFlags_HasGamepad = 1 << 0, // Backend Platform supports gamepad and currently has one connected.
ImGuiBackendFlags_HasMouseCursors = 1 << 1, // Backend Platform supports honoring GetMouseCursor() value to change the OS cursor shape.
ImGuiBackendFlags_HasSetMousePos = 1 << 2, // Backend Platform supports io.WantSetMousePos requests to reposition the OS mouse position (only used if ImGuiConfigFlags_NavEnableSetMousePos is set).
- ImGuiBackendFlags_RendererHasVtxOffset = 1 << 3 // Backend Renderer supports ImDrawCmd::VtxOffset. This enables output of large meshes (64K+ vertices) while still using 16-bit indices.
+ ImGuiBackendFlags_RendererHasVtxOffset = 1 << 3, // Backend Renderer supports ImDrawCmd::VtxOffset. This enables output of large meshes (64K+ vertices) while still using 16-bit indices.
+
+ // [BETA] Viewports
+ ImGuiBackendFlags_PlatformHasViewports = 1 << 10, // Backend Platform supports multiple viewports.
+ 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.
};
// Enumeration for PushStyleColor() / PopStyleColor()
@@ -1564,6 +1637,8 @@ enum ImGuiCol_
ImGuiCol_TabActive,
ImGuiCol_TabUnfocused,
ImGuiCol_TabUnfocusedActive,
+ 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,
ImGuiCol_PlotHistogram,
@@ -1586,7 +1661,7 @@ enum ImGuiCol_
// - The enum only refers to fields of ImGuiStyle which makes sense to be pushed/popped inside UI code.
// During initialization or between frames, feel free to just poke into ImGuiStyle directly.
// - Tip: Use your programming IDE navigation facilities on the names in the _second column_ below to find the actual members and their description.
-// In Visual Studio IDE: CTRL+comma ("Edit.NavigateTo") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
+// In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
// With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments.
// - When changing this enum, you need to update the associated internal table GStyleVarInfo[] accordingly. This is where we link enum values to members offset/type.
enum ImGuiStyleVar_
@@ -1815,7 +1890,7 @@ struct ImVector
inline void pop_back() { IM_ASSERT(Size > 0); Size--; }
inline void push_front(const T& v) { if (Size == 0) push_back(v); else insert(Data, v); }
inline T* erase(const T* it) { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + 1, ((size_t)Size - (size_t)off - 1) * sizeof(T)); Size--; return Data + off; }
- inline T* erase(const T* it, const T* it_last){ IM_ASSERT(it >= Data && it < Data + Size && it_last > it && it_last <= Data + Size); const ptrdiff_t count = it_last - it; const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + count, ((size_t)Size - (size_t)off - (size_t)count) * sizeof(T)); Size -= (int)count; return Data + off; }
+ inline T* erase(const T* it, const T* it_last){ IM_ASSERT(it >= Data && it < Data + Size && it_last >= it && it_last <= Data + Size); const ptrdiff_t count = it_last - it; const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + count, ((size_t)Size - (size_t)off - (size_t)count) * sizeof(T)); Size -= (int)count; return Data + off; }
inline T* erase_unsorted(const T* it) { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; if (it < Data + Size - 1) memcpy(Data + off, Data + Size - 1, sizeof(T)); Size--; return Data + off; }
inline T* insert(const T* it, const T& v) { IM_ASSERT(it >= Data && it <= Data + Size); const ptrdiff_t off = it - Data; if (Size == Capacity) reserve(_grow_capacity(Size + 1)); if (off < (int)Size) memmove(Data + off + 1, Data + off, ((size_t)Size - (size_t)off) * sizeof(T)); memcpy(&Data[off], &v, sizeof(v)); Size++; return Data + off; }
inline bool contains(const T& v) const { const T* data = Data; const T* data_end = Data + Size; while (data < data_end) if (*data++ == v) return true; return false; }
@@ -1871,9 +1946,9 @@ struct ImGuiStyle
ImVec2 SelectableTextAlign; // Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.
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-aliased lines/borders. Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList).
- bool AntiAliasedLinesUseTex; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering. Latched at the beginning of the frame (copied to ImDrawList).
+ bool AntiAliasedLinesUseTex; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). Latched at the beginning of the frame (copied to ImDrawList).
bool AntiAliasedFill; // Enable anti-aliased edges around filled shapes (rounded rectangles, circles, etc.). Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList).
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.
float CircleTessellationMaxError; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
@@ -1903,13 +1978,13 @@ struct ImGuiKeyData
struct ImGuiIO
{
//------------------------------------------------------------------
- // Configuration (fill once) // Default value
+ // Configuration // Default value
//------------------------------------------------------------------
ImGuiConfigFlags ConfigFlags; // = 0 // See ImGuiConfigFlags_ enum. Set by user/application. Gamepad/keyboard navigation options, etc.
ImGuiBackendFlags BackendFlags; // = 0 // See ImGuiBackendFlags_ enum. Set by backend (imgui_impl_xxx files or custom backend) to communicate features supported by the backend.
- ImVec2 DisplaySize; // <unset> // Main display size, in pixels (generally == GetMainViewport()->Size)
- float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds.
+ ImVec2 DisplaySize; // <unset> // Main display size, in pixels (generally == GetMainViewport()->Size). May change every frame.
+ float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds. May change every frame.
float IniSavingRate; // = 5.0f // Minimum time between saving positions/sizes to .ini file, in seconds.
const char* IniFilename; // = "imgui.ini" // Path to .ini file (important: default "imgui.ini" is relative to current working dir!). Set NULL to disable automatic .ini loading/saving or if you want to manually call LoadIniSettingsXXX() / SaveIniSettingsXXX() functions.
const char* LogFilename; // = "imgui_log.txt"// Path to .log file (default parameter to ImGui::LogToFile when no file is specified).
@@ -1926,6 +2001,18 @@ struct ImGuiIO
ImFont* FontDefault; // = NULL // Font to use on NewFrame(). Use NULL to uses Fonts->Fonts[0].
ImVec2 DisplayFramebufferScale; // = (1, 1) // For retina display or other situations where window coordinates are different from framebuffer coordinates. This generally ends up in ImDrawData::FramebufferScale.
+ // 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.
+
+ // 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 // 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 = <main_viewport>, 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
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 backend implementations.
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.
@@ -1973,6 +2060,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 (for multi-viewport support).
IMGUI_API void AddFocusEvent(bool focused); // Queue a gain/loss of focus for the application (generally based on OS/platform focus of your window)
IMGUI_API void AddInputCharacter(unsigned int c); // Queue a new character input
IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue a new character input from an UTF-16 character, it can be a surrogate
@@ -2007,7 +2095,7 @@ struct ImGuiIO
// This is still temporarily supported as a legacy feature. However the new preferred scheme is for backend to call io.AddKeyEvent().
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
int KeyMap[ImGuiKey_COUNT]; // [LEGACY] Input: map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. The first 512 are now unused and should be kept zero. Legacy backend will write into KeyMap[] using ImGuiKey_ indices which are always >512.
- bool KeysDown[512]; // [LEGACY] Input: Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys).
+ bool KeysDown[ImGuiKey_COUNT]; // [LEGACY] Input: Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys). This used to be [512] sized. It is now ImGuiKey_COUNT to allow legacy io.KeysDown[GetKeyIndex(...)] to work without an overflow.
#endif
//------------------------------------------------------------------
@@ -2021,6 +2109,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) 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
@@ -2028,8 +2117,7 @@ struct ImGuiIO
float NavInputs[ImGuiNavInput_COUNT]; // Gamepad inputs. Cleared back to zero by EndFrame(). Keyboard keys will be auto-mapped and be written here by NewFrame().
// Other state maintained from data above + IO function calls
- ImGuiKeyModFlags KeyMods; // Key mods flags (same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags), updated by NewFrame()
- ImGuiKeyModFlags KeyModsPrev; // Key mods flags (from previous frame)
+ ImGuiModFlags KeyMods; // Key mods flags (same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags), updated by NewFrame()
ImGuiKeyData KeysData[ImGuiKey_KeysData_SIZE]; // Key state for all known keys. Use IsKeyXXX() functions to access this.
bool WantCaptureMouseUnlessPopupClose; // Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup.
ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid)
@@ -2041,9 +2129,10 @@ struct ImGuiIO
ImU16 MouseClickedLastCount[5]; // Count successive number of clicks. Stays valid after mouse release. Reset after another click is done.
bool MouseReleased[5]; // Mouse button went from Down to !Down
bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window or over void blocked by a popup. We don't request mouse capture from the application if click started outside ImGui bounds.
- bool MouseDownOwnedUnlessPopupClose[5]; //Track if button was clicked inside a dear imgui window.
+ bool MouseDownOwnedUnlessPopupClose[5]; // Track if button was clicked inside a dear imgui window.
float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked)
float MouseDownDurationPrev[5]; // Previous time the mouse button has been down
+ ImVec2 MouseDragMaxDistanceAbs[5]; // Maximum distance, absolute, on each axis, of how much mouse has traveled from the clicking point
float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the clicking point (used for moving thresholds)
float NavInputsDownDuration[ImGuiNavInput_COUNT];
float NavInputsDownDurationPrev[ImGuiNavInput_COUNT];
@@ -2109,6 +2198,27 @@ struct ImGuiSizeCallbackData
ImVec2 DesiredSize; // Read-write. Desired size, based on user's mouse position. Write to this field to restrain resizing.
};
+// [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 backend via altered viewport flags (enable/disable OS decoration, OS task bar icons, etc.)
+// - To the platform backend 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.
+ 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.
+ 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!)
+ 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)); ParentViewportId = (ImGuiID)-1; DockingAllowUnclassed = true; }
+};
+
// Data payload for Drag and Drop operations: AcceptDragDropPayload(), GetDragDropPayload()
struct ImGuiPayload
{
@@ -2320,6 +2430,8 @@ struct ImGuiListClipper
};
// Helpers macros to generate 32-bit encoded colors
+// User can declare their own format by #defining the 5 _SHIFT/_MASK macros in their imconfig file.
+#ifndef IM_COL32_R_SHIFT
#ifdef IMGUI_USE_BGRA_PACKED_COLOR
#define IM_COL32_R_SHIFT 16
#define IM_COL32_G_SHIFT 8
@@ -2333,6 +2445,7 @@ struct ImGuiListClipper
#define IM_COL32_A_SHIFT 24
#define IM_COL32_A_MASK 0xFF000000
#endif
+#endif
#define IM_COL32(R,G,B,A) (((ImU32)(A)<<IM_COL32_A_SHIFT) | ((ImU32)(B)<<IM_COL32_B_SHIFT) | ((ImU32)(G)<<IM_COL32_G_SHIFT) | ((ImU32)(R)<<IM_COL32_R_SHIFT))
#define IM_COL32_WHITE IM_COL32(255,255,255,255) // Opaque white = 0xFFFFFFFF
#define IM_COL32_BLACK IM_COL32(0,0,0,255) // Opaque black
@@ -2344,13 +2457,13 @@ struct ImGuiListClipper
// **None of the ImGui API are using ImColor directly but you can use it as a convenience to pass colors in either ImU32 or ImVec4 formats. Explicitly cast to ImU32 or ImVec4 if needed.
struct ImColor
{
- ImVec4 Value;
+ ImVec4 Value;
- ImColor() { Value.x = Value.y = Value.z = Value.w = 0.0f; }
+ constexpr ImColor() { }
+ constexpr ImColor(float r, float g, float b, float a = 1.0f) : Value(r, g, b, a) { }
+ constexpr ImColor(const ImVec4& col) : Value(col) {}
ImColor(int r, int g, int b, int a = 255) { float sc = 1.0f / 255.0f; Value.x = (float)r * sc; Value.y = (float)g * sc; Value.z = (float)b * sc; Value.w = (float)a * sc; }
ImColor(ImU32 rgba) { float sc = 1.0f / 255.0f; Value.x = (float)((rgba >> IM_COL32_R_SHIFT) & 0xFF) * sc; Value.y = (float)((rgba >> IM_COL32_G_SHIFT) & 0xFF) * sc; Value.z = (float)((rgba >> IM_COL32_B_SHIFT) & 0xFF) * sc; Value.w = (float)((rgba >> IM_COL32_A_SHIFT) & 0xFF) * sc; }
- ImColor(float r, float g, float b, float a = 1.0f) { Value.x = r; Value.y = g; Value.z = b; Value.w = a; }
- ImColor(const ImVec4& col) { Value = col; }
inline operator ImU32() const { return ImGui::ColorConvertFloat4ToU32(Value); }
inline operator ImVec4() const { return Value; }
@@ -2482,7 +2595,7 @@ enum ImDrawListFlags_
{
ImDrawListFlags_None = 0,
ImDrawListFlags_AntiAliasedLines = 1 << 0, // Enable anti-aliased lines/borders (*2 the number of triangles for 1.0f wide line or lines thin enough to be drawn using textures, otherwise *3 the number of triangles)
- ImDrawListFlags_AntiAliasedLinesUseTex = 1 << 1, // Enable anti-aliased lines/borders using textures when possible. Require backend to render with bilinear filtering.
+ ImDrawListFlags_AntiAliasedLinesUseTex = 1 << 1, // Enable anti-aliased lines/borders using textures when possible. Require backend to render with bilinear filtering (NOT point/nearest filtering).
ImDrawListFlags_AntiAliasedFill = 1 << 2, // Enable anti-aliased edge around filled shapes (rounded rectangles, circles).
ImDrawListFlags_AllowVtxOffset = 1 << 3 // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled.
};
@@ -2521,7 +2634,7 @@ struct ImDrawList
ImDrawList(const ImDrawListSharedData* shared_data) { memset(this, 0, sizeof(*this)); _Data = shared_data; }
~ImDrawList() { _ClearFreeMemory(); }
- IMGUI_API void PushClipRect(ImVec2 clip_rect_min, ImVec2 clip_rect_max, bool intersect_with_current_clip_rect = false); // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling)
+ IMGUI_API void PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect = false); // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling)
IMGUI_API void PushClipRectFullScreen();
IMGUI_API void PopClipRect();
IMGUI_API void PushTextureID(ImTextureID texture_id);
@@ -2530,6 +2643,7 @@ struct ImDrawList
inline ImVec2 GetClipRectMax() const { const ImVec4& cr = _ClipRectStack.back(); return ImVec2(cr.z, cr.w); }
// Primitives
+ // - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing.
// - For rectangular primitives, "p_min" and "p_max" represent the upper-left and lower-right corners.
// - For circle primitives, use "num_segments == 0" to automatically calculate tessellation (preferred).
// In older versions (until Dear ImGui 1.77) the AddCircle functions defaulted to num_segments == 12.
@@ -2550,7 +2664,7 @@ struct ImDrawList
IMGUI_API void AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL);
IMGUI_API void AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL);
IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness);
- IMGUI_API void AddConvexPolyFilled(const ImVec2* points, int num_points, ImU32 col); // Note: Anti-aliased filling requires points to be in clockwise order.
+ IMGUI_API void AddConvexPolyFilled(const ImVec2* points, int num_points, ImU32 col);
IMGUI_API void AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0); // Cubic Bezier (4 control points)
IMGUI_API void AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments = 0); // Quadratic Bezier (3 control points)
@@ -2563,10 +2677,11 @@ struct ImDrawList
IMGUI_API void AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags = 0);
// Stateful path API, add points then finish with PathFillConvex() or PathStroke()
+ // - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing.
inline void PathClear() { _Path.Size = 0; }
inline void PathLineTo(const ImVec2& pos) { _Path.push_back(pos); }
inline void PathLineToMergeDuplicate(const ImVec2& pos) { if (_Path.Size == 0 || memcmp(&_Path.Data[_Path.Size - 1], &pos, 8) != 0) _Path.push_back(pos); }
- inline void PathFillConvex(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; } // Note: Anti-aliased filling requires points to be in clockwise order.
+ inline void PathFillConvex(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; }
inline void PathStroke(ImU32 col, ImDrawFlags flags = 0, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, flags, thickness); _Path.Size = 0; }
IMGUI_API void PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments = 0);
IMGUI_API void PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle
@@ -2632,6 +2747,7 @@ struct ImDrawData
ImVec2 DisplayPos; // Top-left position of the viewport to render (== top-left of the orthogonal projection matrix to use) (== GetMainViewport()->Pos for the main viewport, == (0.0) in most single-viewport applications)
ImVec2 DisplaySize; // Size of the viewport to render (== GetMainViewport()->Size for the main viewport, == io.DisplaySize in most single-viewport applications)
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() { Clear(); }
@@ -2718,7 +2834,7 @@ enum ImFontAtlasFlags_
ImFontAtlasFlags_None = 0,
ImFontAtlasFlags_NoPowerOfTwoHeight = 1 << 0, // Don't round the height to next power of two
ImFontAtlasFlags_NoMouseCursors = 1 << 1, // Don't build software mouse cursors into the atlas (save a little texture memory)
- ImFontAtlasFlags_NoBakedLines = 1 << 2 // Don't build thick line textures into the atlas (save a little texture memory). The AntiAliasedLinesUseTex features uses them, otherwise they will be rendered using polygons (more expensive for CPU/GPU).
+ ImFontAtlasFlags_NoBakedLines = 1 << 2 // Don't build thick line textures into the atlas (save a little texture memory, allow support for point/nearest filtering). The AntiAliasedLinesUseTex features uses them, otherwise they will be rendered using polygons (more expensive for CPU/GPU).
};
// Load and rasterize multiple TTF/OTF fonts into a same texture. The font atlas will build a single texture holding:
@@ -2806,7 +2922,7 @@ struct ImFontAtlas
ImFontAtlasFlags Flags; // Build flags (see ImFontAtlasFlags_)
ImTextureID TexID; // User data to refer to the texture once it has been uploaded to user's graphic systems. It is passed back to you during rendering via the ImDrawCmd structure.
int TexDesiredWidth; // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height.
- int TexGlyphPadding; // Padding between glyphs within texture in pixels. Defaults to 1. If your rendering method doesn't rely on bilinear filtering you may set this to 0.
+ int TexGlyphPadding; // Padding between glyphs within texture in pixels. Defaults to 1. If your rendering method doesn't rely on bilinear filtering you may set this to 0 (will also need to set AntiAliasedLinesUseTex = false).
bool Locked; // Marked as Locked by ImGui::NewFrame() so attempt to modify the atlas will assert.
// [Internal]
@@ -2877,8 +2993,8 @@ struct ImFont
// 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable.
IMGUI_API ImVec2 CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** remaining = NULL) const; // utf8
IMGUI_API const char* CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const;
- IMGUI_API void RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, ImWchar c) const;
- IMGUI_API void RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, bool cpu_fine_clip = false) const;
+ IMGUI_API void RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c) const;
+ IMGUI_API void RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, bool cpu_fine_clip = false) const;
// [Internal] Don't use!
IMGUI_API void BuildLookupTable();
@@ -2900,11 +3016,21 @@ enum ImGuiViewportFlags_
ImGuiViewportFlags_None = 0,
ImGuiViewportFlags_IsPlatformWindow = 1 << 0, // Represent a Platform Window
ImGuiViewportFlags_IsPlatformMonitor = 1 << 1, // Represent a Platform Monitor (unused yet)
- ImGuiViewportFlags_OwnedByApp = 1 << 2 // Platform Window: is created/managed by the application (rather than a dear imgui backend)
+ ImGuiViewportFlags_OwnedByApp = 1 << 2, // Platform Window: is created/managed by the application (rather than a dear imgui backend)
+ ImGuiViewportFlags_NoDecoration = 1 << 3, // 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 << 4, // Platform Window: Disable platform task bar icon (generally set on popups/tooltips, or all windows if ImGuiConfigFlags_ViewportsNoTaskBarIcon is set)
+ ImGuiViewportFlags_NoFocusOnAppearing = 1 << 5, // Platform Window: Don't take focus when created.
+ ImGuiViewportFlags_NoFocusOnClick = 1 << 6, // Platform Window: Don't take focus when clicked on.
+ ImGuiViewportFlags_NoInputs = 1 << 7, // Platform Window: Make mouse pass through so we can drag this window while peaking behind it.
+ ImGuiViewportFlags_NoRendererClear = 1 << 8, // Platform Window: Renderer doesn't need to clear the framebuffer ahead (because we will fill it entirely).
+ ImGuiViewportFlags_TopMost = 1 << 9, // Platform Window: Display on top (for tooltips only).
+ ImGuiViewportFlags_Minimized = 1 << 10, // 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 << 11, // 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 << 12 // Main viewport: can host multiple imgui windows (secondary viewports are associated to a single window).
};
// - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows.
-// - In 'docking' branch with multi-viewport enabled, we extend this concept to have multiple active viewports.
+// - With multi-viewport enabled, we extend this concept to have multiple active viewports.
// - In the future we will extend this concept further to also represent Platform Monitor and support a "no main platform window" operation mode.
// - About Main Area vs Work Area:
// - Main Area = entire viewport.
@@ -2912,16 +3038,31 @@ enum ImGuiViewportFlags_
// - Windows are generally trying to stay within the Work Area of their host viewport.
struct ImGuiViewport
{
+ ImGuiID ID; // Unique identifier for the viewport
ImGuiViewportFlags Flags; // See ImGuiViewportFlags_
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 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().
// Platform/Backend Dependent Data
- void* PlatformHandleRaw; // void* to hold lower-level, platform-native window handle (under Win32 this is expected to be a HWND, unused for other platforms)
+ // 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 (under Win32 this is expected to be a HWND, unused for other platforms), 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); }
// Helpers
ImVec2 GetCenter() const { return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); }
@@ -2929,9 +3070,126 @@ struct ImGuiViewport
};
//-----------------------------------------------------------------------------
-// [SECTION] Platform Dependent Interfaces
+// [SECTION] Platform Dependent Interfaces (for e.g. multi-viewport support)
+//-----------------------------------------------------------------------------
+// [BETA] (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 backend 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 backend from the examples/ folder:
+// - Application: Enable feature with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
+// - Backend: The backend 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 backend:
+// - 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 backends if you can. Either way, carefully follow and refer to examples/ backends for details.
+// - Application: Enable feature with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
+// - Backend: 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 backends.
+// - 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) Access via ImGui::GetPlatformIO()
+struct ImGuiPlatformIO
+{
+ //------------------------------------------------------------------
+ // Input - Backend interface/functions + Monitor List
+ //------------------------------------------------------------------
+
+ // (Optional) Platform functions (e.g. Win32, GLFW, SDL2)
+ // 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 backends 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 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().
+ 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 backend 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 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/backend. 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<ImGuiPlatformMonitor> Monitors;
+
+ //------------------------------------------------------------------
+ // Output - List of viewports to render into platform windows
+ //------------------------------------------------------------------
+
+ // 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")
+ ImVector<ImGuiViewport*> 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; // 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; }
+};
+
// (Optional) Support for IME (Input Method Editor) via the io.SetPlatformImeDataFn() function.
struct ImGuiPlatformImeData
{
@@ -3020,6 +3278,10 @@ enum ImDrawCornerFlags_
ImDrawCornerFlags_Right = ImDrawCornerFlags_TopRight | ImDrawCornerFlags_BotRight
};
+// RENAMED ImGuiKeyModFlags -> ImGuiModFlags in 1.88 (from April 2022)
+typedef int ImGuiKeyModFlags;
+enum ImGuiKeyModFlags_ { ImGuiKeyModFlags_None = ImGuiModFlags_None, ImGuiKeyModFlags_Ctrl = ImGuiModFlags_Ctrl, ImGuiKeyModFlags_Shift = ImGuiModFlags_Shift, ImGuiKeyModFlags_Alt = ImGuiModFlags_Alt, ImGuiKeyModFlags_Super = ImGuiModFlags_Super };
+
#endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
//-----------------------------------------------------------------------------
diff --git a/3rdparty/imgui/source/imgui_demo.cpp b/3rdparty/imgui/source/imgui_demo.cpp
index 72d98ca..3e304c6 100644
--- a/3rdparty/imgui/source/imgui_demo.cpp
+++ b/3rdparty/imgui/source/imgui_demo.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.87
+// dear imgui, v1.88 WIP
// (demo code)
// Help:
@@ -39,7 +39,7 @@
// Because we can't assume anything about your support of maths operators, we cannot use them in imgui_demo.cpp.
// Navigating this file:
-// - In Visual Studio IDE: CTRL+comma ("Edit.NavigateTo") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
+// - In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
// - With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments.
/*
@@ -67,6 +67,7 @@ Index of this file:
// [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen()
// [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()
*/
@@ -171,6 +172,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);
@@ -201,6 +203,16 @@ static void HelpMarker(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 wire demo markers located in code to a interactive browser
typedef void (*ImGuiDemoMarkerCallback)(const char* file, int line, const char* section, void* user_data);
extern ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback;
@@ -271,6 +283,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
// 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_console = false;
@@ -286,7 +299,8 @@ void ImGui::ShowDemoWindow(bool* p_open)
static bool show_app_custom_rendering = false;
if (show_app_main_menu_bar) ShowExampleAppMainMenuBar();
- if (show_app_documents) ShowExampleAppDocuments(&show_app_documents);
+ 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_console) ShowExampleAppConsole(&show_app_console);
if (show_app_log) ShowExampleAppLog(&show_app_log);
@@ -327,6 +341,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
static bool no_nav = false;
static bool no_background = false;
static bool no_bring_to_front = false;
+ static bool no_docking = false;
static bool unsaved_document = false;
ImGuiWindowFlags window_flags = 0;
@@ -339,6 +354,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
if (no_nav) window_flags |= ImGuiWindowFlags_NoNav;
if (no_background) window_flags |= ImGuiWindowFlags_NoBackground;
if (no_bring_to_front) window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus;
+ if (no_docking) window_flags |= ImGuiWindowFlags_NoDocking;
if (unsaved_document) window_flags |= ImGuiWindowFlags_UnsavedDocument;
if (no_close) p_open = NULL; // Don't pass our bool* to Begin
@@ -388,6 +404,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
ImGui::MenuItem("Fullscreen window", NULL, &show_app_fullscreen);
ImGui::MenuItem("Manipulating window titles", NULL, &show_app_window_titles);
ImGui::MenuItem("Custom rendering", NULL, &show_app_custom_rendering);
+ ImGui::MenuItem("Dockspace", NULL, &show_app_dockspace);
ImGui::MenuItem("Documents", NULL, &show_app_documents);
ImGui::EndMenu();
}
@@ -459,6 +476,43 @@ void ImGui::ShowDemoWindow(bool* p_open)
}
ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", &io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange);
ImGui::SameLine(); HelpMarker("Instruct backend to not alter mouse cursor shape and visibility.");
+
+ ImGui::CheckboxFlags("io.ConfigFlags: DockingEnable", &io.ConfigFlags, ImGuiConfigFlags_DockingEnable);
+ 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);
+ 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.");
+ ImGui::Unindent();
+ }
+
+ ImGui::CheckboxFlags("io.ConfigFlags: ViewportsEnable", &io.ConfigFlags, ImGuiConfigFlags_ViewportsEnable);
+ ImGui::SameLine(); HelpMarker("[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(); HelpMarker("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(); HelpMarker("Toggling this at runtime is normally unsupported (most platform backends 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 backends won't refresh the decoration right away).");
+ ImGui::Checkbox("io.ConfigViewportsNoDefaultParent", &io.ConfigViewportsNoDefaultParent);
+ ImGui::SameLine(); HelpMarker("Toggling this at runtime is normally unsupported (most platform backends won't refresh the parenting right away).");
+ ImGui::Unindent();
+ }
+
ImGui::Checkbox("io.ConfigInputTrickleEventQueue", &io.ConfigInputTrickleEventQueue);
ImGui::SameLine(); HelpMarker("Enable input queue trickling: some types of events submitted during the same frame (e.g. button down + up) will be spread over multiple frames, improving interactions with low framerates.");
ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink);
@@ -485,10 +539,13 @@ void ImGui::ShowDemoWindow(bool* p_open)
// Make a local copy to avoid modifying actual backend flags.
// FIXME: We don't use BeginDisabled() to keep label bright, maybe we need a BeginReadonly() equivalent..
ImGuiBackendFlags backend_flags = io.BackendFlags;
- ImGui::CheckboxFlags("io.BackendFlags: HasGamepad", &backend_flags, ImGuiBackendFlags_HasGamepad);
- ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", &backend_flags, ImGuiBackendFlags_HasMouseCursors);
- ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos", &backend_flags, ImGuiBackendFlags_HasSetMousePos);
- ImGui::CheckboxFlags("io.BackendFlags: RendererHasVtxOffset", &backend_flags, ImGuiBackendFlags_RendererHasVtxOffset);
+ ImGui::CheckboxFlags("io.BackendFlags: HasGamepad", &backend_flags, ImGuiBackendFlags_HasGamepad);
+ ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", &backend_flags, ImGuiBackendFlags_HasMouseCursors);
+ ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos", &backend_flags, ImGuiBackendFlags_HasSetMousePos);
+ ImGui::CheckboxFlags("io.BackendFlags: PlatformHasViewports", &backend_flags, ImGuiBackendFlags_PlatformHasViewports);
+ ImGui::CheckboxFlags("io.BackendFlags: HasMouseHoveredViewport",&backend_flags, ImGuiBackendFlags_HasMouseHoveredViewport);
+ ImGui::CheckboxFlags("io.BackendFlags: RendererHasVtxOffset", &backend_flags, ImGuiBackendFlags_RendererHasVtxOffset);
+ ImGui::CheckboxFlags("io.BackendFlags: RendererHasViewports", &backend_flags, ImGuiBackendFlags_RendererHasViewports);
ImGui::TreePop();
ImGui::Separator();
}
@@ -537,6 +594,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
ImGui::TableNextColumn(); ImGui::Checkbox("No nav", &no_nav);
ImGui::TableNextColumn(); ImGui::Checkbox("No background", &no_background);
ImGui::TableNextColumn(); ImGui::Checkbox("No bring to front", &no_bring_to_front);
+ ImGui::TableNextColumn(); ImGui::Checkbox("No docking", &no_docking);
ImGui::TableNextColumn(); ImGui::Checkbox("Unsaved document", &unsaved_document);
ImGui::EndTable();
}
@@ -1855,10 +1913,9 @@ static void ShowDemoWindowWidgets()
ImGui::Combo("Display Mode", &display_mode, "Auto/Current\0None\0RGB Only\0HSV Only\0Hex Only\0");
ImGui::SameLine(); HelpMarker(
"ColorEdit defaults to displaying RGB inputs if you don't specify a display mode, "
- "but the user can change it with a right-click.\n\nColorPicker defaults to displaying RGB+HSV+Hex "
+ "but the user can change it with a right-click on those inputs.\n\nColorPicker defaults to displaying RGB+HSV+Hex "
"if you don't specify a display mode.\n\nYou can change the defaults using SetColorEditOptions().");
- ImGui::Combo("Picker Mode", &picker_mode, "Auto/Current\0Hue bar + SV rect\0Hue wheel + SV triangle\0");
- ImGui::SameLine(); HelpMarker("User can right-click the picker to change mode.");
+ ImGui::SameLine(); HelpMarker("When not specified explicitly (Auto/Current mode), user can right-click the picker to change mode.");
ImGuiColorEditFlags flags = misc_flags;
if (!alpha) flags |= ImGuiColorEditFlags_NoAlpha; // This is by default if you call ColorPicker3() instead of ColorPicker4()
if (alpha_bar) flags |= ImGuiColorEditFlags_AlphaBar;
@@ -1882,6 +1939,15 @@ static void ShowDemoWindowWidgets()
if (ImGui::Button("Default: Float + HDR + Hue Wheel"))
ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_PickerHueWheel);
+ // Always both a small version of both types of pickers (to make it more visible in the demo to people who are skimming quickly through it)
+ ImGui::Text("Both types:");
+ float w = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.y) * 0.40f;
+ ImGui::SetNextItemWidth(w);
+ ImGui::ColorPicker3("##MyColor##5", (float*)&color, ImGuiColorEditFlags_PickerHueBar | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha);
+ ImGui::SameLine();
+ ImGui::SetNextItemWidth(w);
+ ImGui::ColorPicker3("##MyColor##6", (float*)&color, ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha);
+
// HSV encoded support (to avoid RGB<>HSV round trips and singularities when S==0 or V==0)
static ImVec4 color_hsv(0.23f, 1.0f, 1.0f, 1.0f); // Stored as HSV!
ImGui::Spacing();
@@ -2004,6 +2070,7 @@ static void ShowDemoWindowWidgets()
ImGui::DragScalar("drag s16", ImGuiDataType_S16, &s16_v, drag_speed, drag_clamp ? &s16_zero : NULL, drag_clamp ? &s16_fifty : NULL);
ImGui::DragScalar("drag u16", ImGuiDataType_U16, &u16_v, drag_speed, drag_clamp ? &u16_zero : NULL, drag_clamp ? &u16_fifty : NULL, "%u ms");
ImGui::DragScalar("drag s32", ImGuiDataType_S32, &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL);
+ ImGui::DragScalar("drag s32 hex", ImGuiDataType_S32, &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL, "0x%08X");
ImGui::DragScalar("drag u32", ImGuiDataType_U32, &u32_v, drag_speed, drag_clamp ? &u32_zero : NULL, drag_clamp ? &u32_fifty : NULL, "%u ms");
ImGui::DragScalar("drag s64", ImGuiDataType_S64, &s64_v, drag_speed, drag_clamp ? &s64_zero : NULL, drag_clamp ? &s64_fifty : NULL);
ImGui::DragScalar("drag u64", ImGuiDataType_U64, &u64_v, drag_speed, drag_clamp ? &u64_zero : NULL, drag_clamp ? &u64_fifty : NULL);
@@ -2021,6 +2088,7 @@ static void ShowDemoWindowWidgets()
ImGui::SliderScalar("slider s32 low", ImGuiDataType_S32, &s32_v, &s32_zero, &s32_fifty,"%d");
ImGui::SliderScalar("slider s32 high", ImGuiDataType_S32, &s32_v, &s32_hi_a, &s32_hi_b, "%d");
ImGui::SliderScalar("slider s32 full", ImGuiDataType_S32, &s32_v, &s32_min, &s32_max, "%d");
+ ImGui::SliderScalar("slider s32 hex", ImGuiDataType_S32, &s32_v, &s32_zero, &s32_fifty, "0x%04X");
ImGui::SliderScalar("slider u32 low", ImGuiDataType_U32, &u32_v, &u32_zero, &u32_fifty,"%u");
ImGui::SliderScalar("slider u32 high", ImGuiDataType_U32, &u32_v, &u32_hi_a, &u32_hi_b, "%u");
ImGui::SliderScalar("slider u32 full", ImGuiDataType_U32, &u32_v, &u32_min, &u32_max, "%u");
@@ -2054,9 +2122,9 @@ static void ShowDemoWindowWidgets()
ImGui::InputScalar("input s16", ImGuiDataType_S16, &s16_v, inputs_step ? &s16_one : NULL, NULL, "%d");
ImGui::InputScalar("input u16", ImGuiDataType_U16, &u16_v, inputs_step ? &u16_one : NULL, NULL, "%u");
ImGui::InputScalar("input s32", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%d");
- ImGui::InputScalar("input s32 hex", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%08X", ImGuiInputTextFlags_CharsHexadecimal);
+ ImGui::InputScalar("input s32 hex", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%04X");
ImGui::InputScalar("input u32", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%u");
- ImGui::InputScalar("input u32 hex", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%08X", ImGuiInputTextFlags_CharsHexadecimal);
+ ImGui::InputScalar("input u32 hex", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%08X");
ImGui::InputScalar("input s64", ImGuiDataType_S64, &s64_v, inputs_step ? &s64_one : NULL);
ImGui::InputScalar("input u64", ImGuiDataType_U64, &u64_v, inputs_step ? &u64_one : NULL);
ImGui::InputScalar("input float", ImGuiDataType_Float, &f32_v, inputs_step ? &f32_one : NULL);
@@ -2387,18 +2455,24 @@ static void ShowDemoWindowWidgets()
"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) = %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),
ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy),
+ ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy),
ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow));
// Testing IsWindowHovered() function with its various flags.
@@ -2408,10 +2482,13 @@ static void ShowDemoWindowWidgets()
"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(_AnyWindow) = %d\n",
ImGui::IsWindowHovered(),
@@ -2419,10 +2496,13 @@ static void ShowDemoWindowWidgets()
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_AnyWindow));
@@ -2434,10 +2514,13 @@ static void ShowDemoWindowWidgets()
// 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).
static bool test_window = false;
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 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()
{
@@ -3410,7 +3493,7 @@ static void ShowDemoWindowPopups()
{
HelpMarker("Text() elements don't have stable identifiers so we need to provide one.");
static float value = 0.5f;
- ImGui::Text("Value = %.3f <-- (1) right-click this value", value);
+ ImGui::Text("Value = %.3f <-- (1) right-click this text", value);
if (ImGui::BeginPopupContextItem("my popup"))
{
if (ImGui::Selectable("Set to zero")) value = 0.0f;
@@ -3873,7 +3956,7 @@ static void ShowDemoWindowTables()
sprintf(buf, "Hello %d,%d", column, row);
if (contents_type == CT_Text)
ImGui::TextUnformatted(buf);
- else if (contents_type)
+ else if (contents_type == CT_FillButton)
ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f));
}
}
@@ -4417,14 +4500,16 @@ static void ShowDemoWindowTables()
{
ImGui::TableNextColumn();
ImGui::PushID(column);
- ImGui::AlignTextToFramePadding(); // FIXME-TABLE: Workaround for wrong text baseline propagation
+ ImGui::AlignTextToFramePadding(); // FIXME-TABLE: Workaround for wrong text baseline propagation across columns
ImGui::Text("'%s'", column_names[column]);
ImGui::Spacing();
ImGui::Text("Input flags:");
EditTableColumnsFlags(&column_flags[column]);
ImGui::Spacing();
ImGui::Text("Output flags:");
+ ImGui::BeginDisabled();
ShowTableColumnsStatusFlags(column_flags_out[column]);
+ ImGui::EndDisabled();
ImGui::PopID();
}
PopStyleCompact();
@@ -5666,12 +5751,18 @@ static void ShowDemoWindowMisc()
ImGuiIO& io = ImGui::GetIO();
// Display ImGuiIO output flags
- ImGui::Text("WantCaptureMouse: %d", io.WantCaptureMouse);
- ImGui::Text("WantCaptureMouseUnlessPopupClose: %d", io.WantCaptureMouseUnlessPopupClose);
- ImGui::Text("WantCaptureKeyboard: %d", io.WantCaptureKeyboard);
- ImGui::Text("WantTextInput: %d", io.WantTextInput);
- ImGui::Text("WantSetMousePos: %d", io.WantSetMousePos);
- ImGui::Text("NavActive: %d, NavVisible: %d", io.NavActive, io.NavVisible);
+ IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Output");
+ ImGui::SetNextItemOpen(true, ImGuiCond_Once);
+ if (ImGui::TreeNode("Output"))
+ {
+ ImGui::Text("io.WantCaptureMouse: %d", io.WantCaptureMouse);
+ ImGui::Text("io.WantCaptureMouseUnlessPopupClose: %d", io.WantCaptureMouseUnlessPopupClose);
+ ImGui::Text("io.WantCaptureKeyboard: %d", io.WantCaptureKeyboard);
+ ImGui::Text("io.WantTextInput: %d", io.WantTextInput);
+ ImGui::Text("io.WantSetMousePos: %d", io.WantSetMousePos);
+ ImGui::Text("io.NavActive: %d, io.NavVisible: %d", io.NavActive, io.NavVisible);
+ ImGui::TreePop();
+ }
// Display Mouse state
IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Mouse State");
@@ -5704,6 +5795,7 @@ static void ShowDemoWindowMisc()
#else
struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key < 512 && ImGui::GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array
const ImGuiKey key_first = 0;
+ //ImGui::Text("Legacy raw:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key++) { if (io.KeysDown[key]) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } }
#endif
ImGui::Text("Keys down:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key++) { if (funcs::IsLegacyNativeDupe(key)) continue; if (ImGui::IsKeyDown(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (%.02f secs)", ImGui::GetKeyName(key), key, ImGui::GetKeyData(key)->DownDuration); } }
ImGui::Text("Keys pressed:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key++) { if (funcs::IsLegacyNativeDupe(key)) continue; if (ImGui::IsKeyPressed(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } }
@@ -5986,6 +6078,12 @@ void ImGui::ShowAboutWindow(bool* p_open)
#ifdef __clang_version__
ImGui::Text("define: __clang_version__=%s", __clang_version__);
#endif
+#ifdef IMGUI_HAS_VIEWPORT
+ ImGui::Text("define: IMGUI_HAS_VIEWPORT");
+#endif
+#ifdef IMGUI_HAS_DOCK
+ ImGui::Text("define: IMGUI_HAS_DOCK");
+#endif
ImGui::Separator();
ImGui::Text("io.BackendPlatformName: %s", io.BackendPlatformName ? io.BackendPlatformName : "NULL");
ImGui::Text("io.BackendRendererName: %s", io.BackendRendererName ? io.BackendRendererName : "NULL");
@@ -5996,7 +6094,19 @@ 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_ViewportsEnable) ImGui::Text(" ViewportsEnable");
+ 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.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");
if (io.ConfigInputTextCursorBlink) ImGui::Text("io.ConfigInputTextCursorBlink");
if (io.ConfigWindowsResizeFromEdges) ImGui::Text("io.ConfigWindowsResizeFromEdges");
@@ -6006,7 +6116,10 @@ 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");
+ if (io.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) ImGui::Text(" PlatformHasViewports");
+ if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport)ImGui::Text(" HasMouseHoveredViewport");
if (io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) ImGui::Text(" RendererHasVtxOffset");
+ if (io.BackendFlags & ImGuiBackendFlags_RendererHasViewports) ImGui::Text(" RendererHasViewports");
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);
@@ -7246,6 +7359,8 @@ static void ShowExampleAppConstrainedResize(bool* p_open)
if (ImGui::Begin("Example: Constrained Resize", p_open, flags))
{
IMGUI_DEMO_MARKER("Examples/Constrained Resizing window");
+ 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)); }
@@ -7270,7 +7385,7 @@ static void ShowExampleAppSimpleOverlay(bool* p_open)
{
static int corner = 0;
ImGuiIO& io = ImGui::GetIO();
- ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav;
+ ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav;
if (corner != -1)
{
const float PAD = 10.0f;
@@ -7283,6 +7398,7 @@ static void ShowExampleAppSimpleOverlay(bool* p_open)
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
@@ -7624,6 +7740,131 @@ static void ShowExampleAppCustomRendering(bool* p_open)
}
//-----------------------------------------------------------------------------
+// [SECTION] Example App: Docking, DockSpace / ShowExampleAppDockSpace()
+//-----------------------------------------------------------------------------
+
+// Demonstrate using DockSpace() to create an explicit docking node within an existing window.
+// 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.
+// 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)
+{
+ // 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)
+ // - we have a local menu bar in the host window (vs. you could use BeginMainMenuBar() + DockSpaceOverViewport() in your code!)
+ // TL;DR; this demo is more complicated than what you would normally use.
+ // If we removed all the options we are showcasing, this demo would become:
+ // void ShowExampleAppDockSpace()
+ // {
+ // ImGui::DockSpaceOverViewport(ImGui::GetMainViewport());
+ // }
+
+ 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,
+ // because it would be confusing to have two docking targets within each others.
+ ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
+ if (opt_fullscreen)
+ {
+ const ImGuiViewport* viewport = ImGui::GetMainViewport();
+ ImGui::SetNextWindowPos(viewport->WorkPos);
+ ImGui::SetNextWindowSize(viewport->WorkSize);
+ ImGui::SetNextWindowViewport(viewport->ID);
+ ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
+ ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
+ 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.
+ 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.
+ if (!opt_padding)
+ ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
+ ImGui::Begin("DockSpace Demo", p_open, window_flags);
+ if (!opt_padding)
+ ImGui::PopStyleVar();
+
+ if (opt_fullscreen)
+ ImGui::PopStyleVar(2);
+
+ // Submit the 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("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);
+ 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: 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", NULL, false, p_open != NULL))
+ *p_open = false;
+ ImGui::EndMenu();
+ }
+ HelpMarker(
+ "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 (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.");
+
+ ImGui::EndMenuBar();
+ }
+
+ ImGui::End();
+}
+
+//-----------------------------------------------------------------------------
// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments()
//-----------------------------------------------------------------------------
@@ -7722,11 +7963,25 @@ void ShowExampleAppDocuments(bool* p_open)
static ExampleAppDocuments app;
// Options
+ enum Target
+ {
+ 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
+ };
+ 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")
+ // 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)
+ if (!window_contents_visible && opt_target != Target_DockSpaceAndWindow)
{
ImGui::End();
return;
@@ -7773,6 +8028,12 @@ void ShowExampleAppDocuments(bool* p_open)
doc->DoForceClose();
ImGui::PopID();
}
+ ImGui::PushItemWidth(ImGui::GetFontSize() * 12);
+ 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_DockSpaceAndWindow) { ImGui::SameLine(); redock_all = ImGui::Button("Redock all"); }
ImGui::Separator();
@@ -7786,7 +8047,8 @@ void ShowExampleAppDocuments(bool* p_open)
// hole for one-frame, both in the tab-bar and in tab-contents when closing a tab/window.
// The rarely used SetTabItemClosed() function is a way to notify of programmatic closure to avoid the one-frame hole.
- // Submit Tab Bar and Tabs
+ // Tabs
+ if (opt_target == Target_Tab)
{
ImGuiTabBarFlags tab_bar_flags = (opt_fitting_flags) | (opt_reorderable ? ImGuiTabBarFlags_Reorderable : 0);
if (ImGui::BeginTabBar("##tabs", tab_bar_flags))
@@ -7826,6 +8088,53 @@ void ShowExampleAppDocuments(bool* p_open)
ImGui::EndTabBar();
}
}
+ else if (opt_target == Target_DockSpaceAndWindow)
+ {
+ if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_DockingEnable)
+ {
+ 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++)
+ {
+ MyDocument* doc = &app.Documents[doc_n];
+ if (!doc->Open)
+ continue;
+
+ 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);
+
+ ImGui::End();
+ }
+ }
+ else
+ {
+ ShowDockingDisabledMessage();
+ }
+ }
+
+ // Early out other contents
+ if (!window_contents_visible)
+ {
+ ImGui::End();
+ return;
+ }
// Update closing queue
static ImVector<MyDocument*> close_queue;
diff --git a/3rdparty/imgui/source/imgui_draw.cpp b/3rdparty/imgui/source/imgui_draw.cpp
index a99e6b2..84fd114 100644
--- a/3rdparty/imgui/source/imgui_draw.cpp
+++ b/3rdparty/imgui/source/imgui_draw.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.87
+// dear imgui, v1.88 WIP
// (drawing and font code)
/*
@@ -90,7 +90,7 @@ Index of this file:
#endif
//-------------------------------------------------------------------------
-// [SECTION] STB libraries implementation
+// [SECTION] STB libraries implementation (for stb_truetype and stb_rect_pack)
//-------------------------------------------------------------------------
// Compile time options:
@@ -230,6 +230,8 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst)
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_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_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);
@@ -290,6 +292,8 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst)
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_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_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);
@@ -351,6 +355,8 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst)
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_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_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);
@@ -405,6 +411,8 @@ void ImDrawList::_ResetForNewFrame()
IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, ClipRect) == 0);
IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, TextureId) == sizeof(ImVec4));
IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID));
+ if (_Splitter._Count > 1)
+ _Splitter.Merge(this);
CmdBuffer.resize(0);
IdxBuffer.resize(0);
@@ -580,7 +588,7 @@ int ImDrawList::_CalcCircleAutoSegmentCount(float radius) const
}
// Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling)
-void ImDrawList::PushClipRect(ImVec2 cr_min, ImVec2 cr_max, bool intersect_with_current_clip_rect)
+void ImDrawList::PushClipRect(const ImVec2& cr_min, const ImVec2& cr_max, bool intersect_with_current_clip_rect)
{
ImVec4 cr(cr_min.x, cr_min.y, cr_max.x, cr_max.y);
if (intersect_with_current_clip_rect)
@@ -973,7 +981,8 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
}
}
-// We intentionally avoid using ImVec2 and its math operators here to reduce cost to a minimum for debug/non-inlined builds.
+// - We intentionally avoid using ImVec2 and its math operators here to reduce cost to a minimum for debug/non-inlined builds.
+// - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing.
void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_count, ImU32 col)
{
if (points_count < 3)
@@ -1206,8 +1215,8 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa
const float a_min_segment_angle = a_min_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
const float a_max_segment_angle = a_max_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
- const bool a_emit_start = (a_min_segment_angle - a_min) != 0.0f;
- const bool a_emit_end = (a_max - a_max_segment_angle) != 0.0f;
+ const bool a_emit_start = ImAbs(a_min_segment_angle - a_min) >= 1e-5f;
+ const bool a_emit_end = ImAbs(a_max - a_max_segment_angle) >= 1e-5f;
_Path.reserve(_Path.Size + (a_mid_samples + 1 + (a_emit_start ? 1 : 0) + (a_emit_end ? 1 : 0)));
if (a_emit_start)
@@ -2637,8 +2646,8 @@ void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opa
for (int i = 0; i < pack_rects.Size; i++)
if (pack_rects[i].was_packed)
{
- user_rects[i].X = pack_rects[i].x;
- user_rects[i].Y = pack_rects[i].y;
+ user_rects[i].X = (unsigned short)pack_rects[i].x;
+ user_rects[i].Y = (unsigned short)pack_rects[i].y;
IM_ASSERT(pack_rects[i].w == user_rects[i].Width && pack_rects[i].h == user_rects[i].Height);
atlas->TexHeight = ImMax(atlas->TexHeight, pack_rects[i].y + pack_rects[i].h);
}
@@ -2738,13 +2747,13 @@ static void ImFontAtlasBuildRenderLinesTexData(ImFontAtlas* atlas)
{
unsigned int* write_ptr = &atlas->TexPixelsRGBA32[r->X + ((r->Y + y) * atlas->TexWidth)];
for (unsigned int i = 0; i < pad_left; i++)
- *(write_ptr + i) = IM_COL32_BLACK_TRANS;
+ *(write_ptr + i) = IM_COL32(255, 255, 255, 0);
for (unsigned int i = 0; i < line_width; i++)
*(write_ptr + pad_left + i) = IM_COL32_WHITE;
for (unsigned int i = 0; i < pad_right; i++)
- *(write_ptr + pad_left + line_width + i) = IM_COL32_BLACK_TRANS;
+ *(write_ptr + pad_left + line_width + i) = IM_COL32(255, 255, 255, 0);
}
// Calculate UVs for this line
@@ -3523,7 +3532,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
}
// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
-void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, ImWchar c) const
+void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c) const
{
const ImFontGlyph* glyph = FindGlyph(c);
if (!glyph || !glyph->Visible)
@@ -3531,26 +3540,25 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
if (glyph->Colored)
col |= ~IM_COL32_A_MASK;
float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f;
- pos.x = IM_FLOOR(pos.x);
- pos.y = IM_FLOOR(pos.y);
+ float x = IM_FLOOR(pos.x);
+ float y = IM_FLOOR(pos.y);
draw_list->PrimReserve(6, 4);
- draw_list->PrimRectUV(ImVec2(pos.x + glyph->X0 * scale, pos.y + glyph->Y0 * scale), ImVec2(pos.x + glyph->X1 * scale, pos.y + glyph->Y1 * scale), ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V1), col);
+ draw_list->PrimRectUV(ImVec2(x + glyph->X0 * scale, y + glyph->Y0 * scale), ImVec2(x + glyph->X1 * scale, y + glyph->Y1 * scale), ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V1), col);
}
// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
-void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) const
+void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) const
{
if (!text_end)
text_end = text_begin + strlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls.
// Align to be pixel perfect
- pos.x = IM_FLOOR(pos.x);
- pos.y = IM_FLOOR(pos.y);
- float x = pos.x;
- float y = pos.y;
+ float x = IM_FLOOR(pos.x);
+ float y = IM_FLOOR(pos.y);
if (y > clip_rect.w)
return;
+ const float start_x = x;
const float scale = size / FontSize;
const float line_height = FontSize * scale;
const bool word_wrap_enabled = (wrap_width > 0.0f);
@@ -3602,14 +3610,14 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
// Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
if (!word_wrap_eol)
{
- word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - (x - pos.x));
+ word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - (x - start_x));
if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.
word_wrap_eol++; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below
}
if (s >= word_wrap_eol)
{
- x = pos.x;
+ x = start_x;
y += line_height;
word_wrap_eol = NULL;
@@ -3640,7 +3648,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
{
if (c == '\n')
{
- x = pos.x;
+ x = start_x;
y += line_height;
if (y > clip_rect.w)
break; // break out of main loop
@@ -3736,7 +3744,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
// - RenderArrow()
// - RenderBullet()
// - RenderCheckMark()
-// - RenderMouseCursor()
+// - RenderArrowDockMenu()
// - RenderArrowPointingAt()
// - RenderRectFilledRangeH()
// - RenderRectFilledWithHole()
@@ -3797,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)
{
@@ -3831,6 +3818,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.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)
{
if (x <= 0.0f) return IM_PI * 0.5f;
@@ -3900,7 +3895,7 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im
draw_list->PathFillConvex(col);
}
-void ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, ImRect outer, ImRect inner, ImU32 col, float rounding)
+void ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer, const 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);
@@ -3916,6 +3911,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/3rdparty/imgui/source/imgui_internal.h b/3rdparty/imgui/source/imgui_internal.h
index baa4a4d..6523306 100644
--- a/3rdparty/imgui/source/imgui_internal.h
+++ b/3rdparty/imgui/source/imgui_internal.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.87
+// dear imgui, v1.88 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!
@@ -118,6 +118,10 @@ struct ImGuiColorMod; // Stacked color modifier, backup of modifie
struct ImGuiContext; // Main Dear ImGui context
struct ImGuiContextHook; // Hook for extensions like ImGuiTestEngine
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()
struct ImGuiInputTextState; // Internal state of the currently focused/edited text input box
struct ImGuiLastItemData; // Status storage for last submitted items
@@ -136,6 +140,7 @@ struct ImGuiTabBar; // Storage for a tab bar
struct ImGuiTabItem; // Storage for a tab item (within a tab bar)
struct ImGuiTable; // Storage for a table
struct ImGuiTableColumn; // Storage for one column of a table
+struct ImGuiTableInstanceData; // Storage for one instance of a same table
struct ImGuiTableTempData; // Temporary storage for one table (one per table in the stack), shared between tables.
struct ImGuiTableSettings; // Storage for a table .ini settings
struct ImGuiTableColumnsSettings; // Storage for a column .ini settings
@@ -144,6 +149,7 @@ struct ImGuiWindowTempData; // Temporary storage for one window (that's
struct ImGuiWindowSettings; // Storage for a window .ini settings (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 ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later)
typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag()
@@ -192,6 +198,9 @@ namespace ImStb
// [SECTION] Macros
//-----------------------------------------------------------------------------
+// Internal Drag and Drop payload types. String starting with '_' are reserved for Dear ImGui.
+#define IMGUI_PAYLOAD_TYPE_WINDOW "_IMWINDOW" // Payload == ImGuiWindow*
+
// Debug Logging
#ifndef IMGUI_DEBUG_LOG
#define IMGUI_DEBUG_LOG(_FMT,...) printf("[%05d] " _FMT, GImGui->FrameCount, __VA_ARGS__)
@@ -201,9 +210,13 @@ namespace ImStb
//#define IMGUI_DEBUG_LOG_POPUP IMGUI_DEBUG_LOG // Enable log
//#define IMGUI_DEBUG_LOG_NAV IMGUI_DEBUG_LOG // Enable log
//#define IMGUI_DEBUG_LOG_IO 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_NAV(...) ((void)0) // Disable log
#define IMGUI_DEBUG_LOG_IO(...) ((void)0) // Disable log
+#define IMGUI_DEBUG_LOG_VIEWPORT(...) ((void)0) // Disable log
+#define IMGUI_DEBUG_LOG_DOCKING(...) ((void)0) // Disable log
// Static Asserts
#define IM_STATIC_ASSERT(_COND) static_assert(_COND, "")
@@ -231,7 +244,7 @@ namespace ImStb
#define IM_NEWLINE "\n"
#endif
#define IM_TABSIZE (4)
-#define IM_MEMALIGN(_OFF,_ALIGN) (((_OFF) + (_ALIGN - 1)) & ~(_ALIGN - 1)) // Memory align e.g. IM_ALIGN(0,4)=0, IM_ALIGN(1,4)=4, IM_ALIGN(4,4)=4, IM_ALIGN(5,4)=8
+#define IM_MEMALIGN(_OFF,_ALIGN) (((_OFF) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1)) // Memory align e.g. IM_ALIGN(0,4)=0, IM_ALIGN(1,4)=4, IM_ALIGN(4,4)=4, IM_ALIGN(5,4)=8
#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
#define IM_FLOOR(_VAL) ((float)(int)(_VAL)) // ImFloor() is not inlined in MSVC debug builds
@@ -278,7 +291,8 @@ namespace ImStb
// - Helpers: Hashing
// - Helpers: Sorting
// - Helpers: Bit manipulation
-// - Helpers: String, Formatting
+// - Helpers: String
+// - Helpers: Formatting
// - Helpers: UTF-8 <> wchar conversions
// - Helpers: ImVec2/ImVec4 operators
// - Helpers: Maths
@@ -296,9 +310,6 @@ namespace ImStb
// Helpers: Hashing
IMGUI_API ImGuiID ImHashData(const void* data, size_t data_size, ImU32 seed = 0);
IMGUI_API ImGuiID ImHashStr(const char* data, size_t data_size = 0, ImU32 seed = 0);
-#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
-static inline ImGuiID ImHash(const void* data, int size, ImU32 seed = 0) { return size ? ImHashData(data, (size_t)size, seed) : ImHashStr((const char*)data, 0, seed); } // [moved to ImHashStr/ImHashData in 1.68]
-#endif
// Helpers: Sorting
#ifndef ImQsort
@@ -313,7 +324,7 @@ static inline bool ImIsPowerOfTwo(int v) { return v != 0 && (v &
static inline bool ImIsPowerOfTwo(ImU64 v) { return v != 0 && (v & (v - 1)) == 0; }
static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; }
-// Helpers: String, Formatting
+// Helpers: String
IMGUI_API int ImStricmp(const char* str1, const char* str2);
IMGUI_API int ImStrnicmp(const char* str1, const char* str2, size_t count);
IMGUI_API void ImStrncpy(char* dst, const char* src, size_t count);
@@ -326,14 +337,18 @@ IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* bu
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);
+static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; }
+static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; }
+
+// Helpers: Formatting
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);
IMGUI_API const char* ImParseFormatFindEnd(const char* format);
IMGUI_API const char* ImParseFormatTrimDecorations(const char* format, char* buf, size_t buf_size);
+IMGUI_API void ImParseFormatSanitizeForPrinting(const char* fmt_in, char* fmt_out, size_t fmt_out_size);
+IMGUI_API const char* ImParseFormatSanitizeForScanning(const char* fmt_in, char* fmt_out, size_t fmt_out_size);
IMGUI_API int ImParseFormatPrecision(const char* format, int default_value);
-static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; }
-static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; }
// Helpers: UTF-8 <> wchar conversions
IMGUI_API const char* ImTextCharToUtf8(char out_buf[5], unsigned int c); // return out_buf
@@ -469,17 +484,17 @@ IM_MSVC_RUNTIME_CHECKS_OFF
struct ImVec1
{
float x;
- ImVec1() { x = 0.0f; }
- ImVec1(float _x) { x = _x; }
+ constexpr ImVec1() : x(0.0f) { }
+ constexpr ImVec1(float _x) : x(_x) { }
};
// Helper: ImVec2ih (2D vector, half-size integer, for long-term packed storage)
struct ImVec2ih
{
short x, 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; }
+ constexpr ImVec2ih() : x(0), y(0) {}
+ constexpr ImVec2ih(short _x, short _y) : x(_x), y(_y) {}
+ constexpr explicit ImVec2ih(const ImVec2& rhs) : x((short)rhs.x), y((short)rhs.y) {}
};
// Helper: ImRect (2D axis aligned bounding-box)
@@ -489,10 +504,10 @@ struct IMGUI_API ImRect
ImVec2 Min; // Upper-left
ImVec2 Max; // Lower-right
- ImRect() : Min(0.0f, 0.0f), Max(0.0f, 0.0f) {}
- ImRect(const ImVec2& min, const ImVec2& max) : Min(min), Max(max) {}
- ImRect(const ImVec4& v) : Min(v.x, v.y), Max(v.z, v.w) {}
- ImRect(float x1, float y1, float x2, float y2) : Min(x1, y1), Max(x2, y2) {}
+ constexpr ImRect() : Min(0.0f, 0.0f), Max(0.0f, 0.0f) {}
+ constexpr ImRect(const ImVec2& min, const ImVec2& max) : Min(min), Max(max) {}
+ constexpr ImRect(const ImVec4& v) : Min(v.x, v.y), Max(v.z, v.w) {}
+ constexpr ImRect(float x1, float y1, float x2, float y2) : Min(x1, y1), Max(x2, y2) {}
ImVec2 GetCenter() const { return ImVec2((Min.x + Max.x) * 0.5f, (Min.y + Max.y) * 0.5f); }
ImVec2 GetSize() const { return ImVec2(Max.x - Min.x, Max.y - Min.y); }
@@ -547,11 +562,11 @@ struct ImBitArray
ImBitArray() { ClearAllBits(); }
void ClearAllBits() { memset(Storage, 0, sizeof(Storage)); }
void SetAllBits() { memset(Storage, 255, sizeof(Storage)); }
- bool TestBit(int n) const { IM_ASSERT(n + OFFSET < BITCOUNT); return ImBitArrayTestBit(Storage, n + OFFSET); }
- void SetBit(int n) { IM_ASSERT(n + OFFSET < BITCOUNT); ImBitArraySetBit(Storage, n + OFFSET); }
- void ClearBit(int n) { IM_ASSERT(n + OFFSET < BITCOUNT); ImBitArrayClearBit(Storage, n + OFFSET); }
- void SetBitRange(int n, int n2) { ImBitArraySetBitRange(Storage, n + OFFSET, n2 + OFFSET); } // Works on range [n..n2)
- bool operator[](int n) const { IM_ASSERT(n + OFFSET < BITCOUNT); return ImBitArrayTestBit(Storage, n + OFFSET); }
+ bool TestBit(int n) const { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return ImBitArrayTestBit(Storage, n); }
+ void SetBit(int n) { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArraySetBit(Storage, n); }
+ void ClearBit(int n) { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArrayClearBit(Storage, n); }
+ void SetBitRange(int n, int n2) { n += OFFSET; n2 += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT && n2 > n && n2 <= BITCOUNT); ImBitArraySetBitRange(Storage, n, n2); } // Works on range [n..n2)
+ bool operator[](int n) const { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return ImBitArrayTestBit(Storage, n); }
};
// Helper: ImBitVector
@@ -775,9 +790,9 @@ enum ImGuiItemStatusFlags_
#ifdef IMGUI_ENABLE_TEST_ENGINE
, // [imgui_tests only]
- ImGuiItemStatusFlags_Openable = 1 << 20, //
+ ImGuiItemStatusFlags_Openable = 1 << 20, // Item is an openable (e.g. TreeNode)
ImGuiItemStatusFlags_Opened = 1 << 21, //
- ImGuiItemStatusFlags_Checkable = 1 << 22, //
+ ImGuiItemStatusFlags_Checkable = 1 << 22, // Item is a checkable (e.g. CheckBox, MenuItem)
ImGuiItemStatusFlags_Checked = 1 << 23 //
#endif
};
@@ -930,8 +945,8 @@ enum ImGuiDataTypePrivate_
// Stacked color modifier, backup of modified data so we can restore it
struct ImGuiColorMod
{
- ImGuiCol Col;
- ImVec4 BackupValue;
+ ImGuiCol Col;
+ ImVec4 BackupValue;
};
// Stacked style modifier, backup of modified data so we can restore it. Data type inferred from the variable.
@@ -1052,7 +1067,10 @@ enum ImGuiNextWindowDataFlags_
ImGuiNextWindowDataFlags_HasSizeConstraint = 1 << 4,
ImGuiNextWindowDataFlags_HasFocus = 1 << 5,
ImGuiNextWindowDataFlags_HasBgAlpha = 1 << 6,
- ImGuiNextWindowDataFlags_HasScroll = 1 << 7
+ ImGuiNextWindowDataFlags_HasScroll = 1 << 7,
+ ImGuiNextWindowDataFlags_HasViewport = 1 << 8,
+ ImGuiNextWindowDataFlags_HasDock = 1 << 9,
+ ImGuiNextWindowDataFlags_HasWindowClass = 1 << 10
};
// Storage for SetNexWindow** functions
@@ -1062,16 +1080,21 @@ struct ImGuiNextWindowData
ImGuiCond PosCond;
ImGuiCond SizeCond;
ImGuiCond CollapsedCond;
+ ImGuiCond DockCond;
ImVec2 PosVal;
ImVec2 PosPivotVal;
ImVec2 SizeVal;
ImVec2 ContentSizeVal;
ImVec2 ScrollVal;
+ bool PosUndock;
bool CollapsedVal;
ImRect SizeConstraintRect;
ImGuiSizeCallback SizeCallback;
void* SizeCallbackUserData;
float BgAlphaVal; // Override background alpha
+ ImGuiID ViewportId;
+ ImGuiID DockId;
+ ImGuiWindowClass WindowClass;
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)); }
@@ -1170,8 +1193,9 @@ enum ImGuiInputEventType
ImGuiInputEventType_MousePos,
ImGuiInputEventType_MouseWheel,
ImGuiInputEventType_MouseButton,
+ ImGuiInputEventType_MouseViewport,
ImGuiInputEventType_Key,
- ImGuiInputEventType_Char,
+ ImGuiInputEventType_Text,
ImGuiInputEventType_Focus,
ImGuiInputEventType_COUNT
};
@@ -1192,6 +1216,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; float AnalogValue; };
struct ImGuiInputEventText { unsigned int Char; };
struct ImGuiInputEventAppFocused { bool Focused; };
@@ -1205,6 +1230,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
ImGuiInputEventText Text; // if Type == ImGuiInputEventType_Text
ImGuiInputEventAppFocused AppFocused; // if Type == ImGuiInputEventType_Focus
@@ -1293,7 +1319,7 @@ enum ImGuiNavHighlightFlags_
enum ImGuiNavDirSourceFlags_
{
ImGuiNavDirSourceFlags_None = 0,
- ImGuiNavDirSourceFlags_RawKeyboard = 1 << 0, // Raw keyboard (not pulled from nav), faciliate use of some functions before we can unify nav and keys
+ ImGuiNavDirSourceFlags_RawKeyboard = 1 << 0, // Raw keyboard (not pulled from nav), facilitate use of some functions before we can unify nav and keys
ImGuiNavDirSourceFlags_Keyboard = 1 << 1,
ImGuiNavDirSourceFlags_PadDPad = 1 << 2,
ImGuiNavDirSourceFlags_PadLStick = 1 << 3
@@ -1408,7 +1434,140 @@ struct ImGuiOldColumns
//-----------------------------------------------------------------------------
#ifdef IMGUI_HAS_DOCK
-// <this is filled in 'docking' branch>
+
+// Extend ImGuiDockNodeFlags_
+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 // 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)
+ 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 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,
+ ImGuiDockNodeFlags_LocalFlagsTransferMask_ = ImGuiDockNodeFlags_LocalFlagsMask_ & ~ImGuiDockNodeFlags_DockSpace, // When splitting those flags are moved to the inheriting child, never duplicated
+ 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
+enum ImGuiDataAuthority_
+{
+ ImGuiDataAuthority_Auto,
+ ImGuiDataAuthority_DockNode,
+ ImGuiDataAuthority_Window
+};
+
+enum ImGuiDockNodeState
+{
+ ImGuiDockNodeState_Unknown,
+ ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow,
+ ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing,
+ ImGuiDockNodeState_HostWindowVisible
+};
+
+// sizeof() 156~192
+struct IMGUI_API ImGuiDockNode
+{
+ ImGuiID ID;
+ 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.
+ ImVector<ImGuiWindow*> 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.
+ 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.
+ 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.
+ 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;
+ 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;
+ 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);
+ ~ImGuiDockNode();
+ bool IsRootNode() const { return ParentNode == NULL; }
+ 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; }
+ 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.
+// 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
+ ImVector<ImGuiDockRequest> Requests;
+ ImVector<ImGuiDockNodeSettings> NodesSettings;
+ bool WantFullRebuild;
+ ImGuiDockContext() { memset(this, 0, sizeof(*this)); }
+};
+
#endif // #ifdef IMGUI_HAS_DOCK
//-----------------------------------------------------------------------------
@@ -1419,18 +1578,31 @@ struct ImGuiOldColumns
// 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 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 LastAlpha;
+ short PlatformMonitor;
+ bool PlatformWindowCreated;
+ 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;
+ ImVec2 LastPlatformSize;
+ ImVec2 LastRendererSize;
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 BuildWorkOffsetMin; // Work Area: Offset being built during current frame. Generally >= 0.0f.
ImVec2 BuildWorkOffsetMax; // Work Area: Offset being built during current frame. Generally <= 0.0f.
- ImGuiViewportP() { DrawListsLastFrame[0] = DrawListsLastFrame[1] = -1; DrawLists[0] = DrawLists[1] = NULL; }
- ~ImGuiViewportP() { if (DrawLists[0]) IM_DELETE(DrawLists[0]); if (DrawLists[1]) IM_DELETE(DrawLists[1]); }
+ 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]); }
+ void ClearRequestFlags() { PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; }
// Calculate work rect pos/size given a set of offset (we have 1 pair of offset for rect locked from last frame data, and 1 pair for currently building rect)
ImVec2 CalcWorkRectPos(const ImVec2& off_min) const { return ImVec2(Pos.x + off_min.x, Pos.y + off_min.y); }
@@ -1453,12 +1625,17 @@ struct ImGuiViewportP : public ImGuiViewport
struct ImGuiWindowSettings
{
ImGuiID ID;
- ImVec2ih Pos;
+ ImVec2ih Pos; // NB: Settings position are stored RELATIVE to the viewport! Whereas runtime ones are absolute positions.
ImVec2ih Size;
+ 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;
bool WantApply; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context)
- ImGuiWindowSettings() { memset(this, 0, sizeof(*this)); }
+ ImGuiWindowSettings() { memset(this, 0, sizeof(*this)); DockOrder = -1; }
char* GetName() { return (char*)(this + 1); }
};
@@ -1489,6 +1666,7 @@ struct ImGuiMetricsConfig
bool ShowTablesRects;
bool ShowDrawCmdMesh;
bool ShowDrawCmdBoundingBoxes;
+ bool ShowDockingNodes;
int ShowWindowsRectsType;
int ShowTablesRectsType;
@@ -1500,6 +1678,7 @@ struct ImGuiMetricsConfig
ShowTablesRects = false;
ShowDrawCmdMesh = true;
ShowDrawCmdBoundingBoxes = true;
+ ShowDockingNodes = false;
ShowWindowsRectsType = -1;
ShowTablesRectsType = -1;
}
@@ -1510,7 +1689,8 @@ struct ImGuiStackLevelInfo
ImGuiID ID;
ImS8 QueryFrameCount; // >= 1: Query in progress
bool QuerySuccess; // Obtained result from DebugHookIdInfo()
- char Desc[58]; // Arbitrarily sized buffer to hold a result (FIXME: could replace Results[] with a chunk stream?)
+ ImGuiDataType DataType : 8;
+ char Desc[57]; // Arbitrarily sized buffer to hold a result (FIXME: could replace Results[] with a chunk stream?) FIXME: Now that we added CTRL+C this should be fixed.
ImGuiStackLevelInfo() { memset(this, 0, sizeof(*this)); }
};
@@ -1522,8 +1702,10 @@ struct ImGuiStackTool
int StackLevel; // -1: query stack and resize Results, >= 0: individual stack level
ImGuiID QueryId; // ID to query details for
ImVector<ImGuiStackLevelInfo> Results;
+ bool CopyToClipboardOnCtrlC;
+ float CopyToClipboardLastTime;
- ImGuiStackTool() { memset(this, 0, sizeof(*this)); }
+ ImGuiStackTool() { memset(this, 0, sizeof(*this)); CopyToClipboardLastTime = -FLT_MAX; }
};
//-----------------------------------------------------------------------------
@@ -1545,7 +1727,7 @@ struct ImGuiContextHook
};
//-----------------------------------------------------------------------------
-// [SECTION] ImGuiContext (main imgui context)
+// [SECTION] ImGuiContext (main Dear ImGui context)
//-----------------------------------------------------------------------------
struct ImGuiContext
@@ -1553,9 +1735,12 @@ struct ImGuiContext
bool Initialized;
bool FontAtlasOwnedByContext; // IO.Fonts-> is owned by the ImGuiContext and will be destructed along with it.
ImGuiIO IO;
+ ImGuiPlatformIO PlatformIO;
ImVector<ImGuiInputEvent> InputEventsQueue; // Input events which will be tricked/written into IO structure.
ImVector<ImGuiInputEvent> InputEventsTrail; // Past input events processed in NewFrame(). This is to allow domain-specific application to access e.g mouse/pen trail.
ImGuiStyle Style;
+ 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.
@@ -1563,6 +1748,7 @@ struct ImGuiContext
double Time;
int FrameCount;
int FrameCountEnded;
+ int FrameCountPlatformEnded;
int FrameCountRendered;
bool WithinFrameScope; // Set by NewFrame(), cleared by EndFrame()
bool WithinFrameScopeWithImplicitWindow; // Set by NewFrame(), cleared by EndFrame() when the implicit debug window has been pushed
@@ -1582,7 +1768,8 @@ 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.
- ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindow.
+ 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;
float WheelingWindowTimer;
@@ -1639,10 +1826,17 @@ struct ImGuiContext
int BeginMenuCount;
// Viewports
- ImVector<ImGuiViewportP*> Viewports; // Active viewports (Size==1 in 'master' branch). Each viewports hold their copy of ImDrawData.
+ ImVector<ImGuiViewportP*> 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.
+ 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
- ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusWindow'
+ ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusedWindow'
ImGuiID NavId; // Focused item for navigation
ImGuiID NavFocusScopeId; // Identify a selection scope (selection code often wants to "clear other items" when landing on an item of the selection set)
ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0, also set when calling ActivateItem()
@@ -1652,7 +1846,7 @@ struct ImGuiContext
ImGuiActivateFlags NavActivateFlags;
ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest).
ImGuiID NavJustMovedToFocusScopeId; // Just navigated to this focus scope id (result of a successfully MoveRequest).
- ImGuiKeyModFlags NavJustMovedToKeyMods;
+ ImGuiModFlags NavJustMovedToKeyMods;
ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame.
ImGuiActivateFlags NavNextActivateFlags;
ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS WILL ONLY BE None or NavGamepad or NavKeyboard.
@@ -1673,7 +1867,7 @@ struct ImGuiContext
bool NavMoveForwardToNextFrame;
ImGuiNavMoveFlags NavMoveFlags;
ImGuiScrollFlags NavMoveScrollFlags;
- ImGuiKeyModFlags NavMoveKeyMods;
+ ImGuiModFlags NavMoveKeyMods;
ImGuiDir NavMoveDir; // Direction of the move request (left/right/up/down)
ImGuiDir NavMoveDirForDebug;
ImGuiDir NavMoveClipDir; // FIXME-NAV: Describe the purpose of this better. Might want to rename?
@@ -1722,7 +1916,7 @@ struct ImGuiContext
int ClipperTempDataStacked;
ImVector<ImGuiListClipperData> ClipperTempData;
- // Table
+ // Tables
ImGuiTable* CurrentTable;
int TablesTempDataStacked; // Temporary table data size (because we leave previous instances undestructed, we generally don't use TablesTempData.Size)
ImVector<ImGuiTableTempData> TablesTempData; // Temporary table data (buffers reused/shared across instances, support nesting)
@@ -1763,8 +1957,13 @@ struct ImGuiContext
// Platform support
ImGuiPlatformImeData PlatformImeData; // Data updated by current frame
ImGuiPlatformImeData PlatformImeDataPrev; // Previous frame data (when changing we will call io.SetPlatformImeDataFn
+ ImGuiID PlatformImeViewport;
char PlatformLocaleDecimalPoint; // '.' or *localeconv()->decimal_point
+ // 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
@@ -1807,13 +2006,14 @@ struct ImGuiContext
ImGuiContext(ImFontAtlas* shared_font_atlas)
{
Initialized = false;
+ ConfigFlagsCurrFrame = ConfigFlagsLastFrame = ImGuiConfigFlags_None;
FontAtlasOwnedByContext = shared_font_atlas ? false : true;
Font = NULL;
FontSize = FontBaseSize = 0.0f;
IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)();
Time = 0.0f;
FrameCount = 0;
- FrameCountEnded = FrameCountRendered = -1;
+ FrameCountEnded = FrameCountPlatformEnded = FrameCountRendered = -1;
WithinFrameScope = WithinFrameScopeWithImplicitWindow = WithinEndChild = false;
GcCompactAll = false;
TestEngineHookItems = false;
@@ -1823,6 +2023,7 @@ struct ImGuiContext
CurrentWindow = NULL;
HoveredWindow = NULL;
HoveredWindowUnderMovingWindow = NULL;
+ HoveredDockNode = NULL;
MovingWindow = NULL;
WheelingWindow = NULL;
WheelingWindowTimer = 0.0f;
@@ -1860,11 +2061,17 @@ struct ImGuiContext
CurrentItemFlags = ImGuiItemFlags_None;
BeginMenuCount = 0;
+ CurrentDpiScale = 0.0f;
+ CurrentViewport = NULL;
+ MouseViewport = MouseLastHoveredViewport = NULL;
+ PlatformLastFocusedViewportId = 0;
+ ViewportFrontMostStampCount = 0;
+
NavWindow = NULL;
NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavActivateInputId = 0;
NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0;
NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None;
- NavJustMovedToKeyMods = ImGuiKeyModFlags_None;
+ NavJustMovedToKeyMods = ImGuiModFlags_None;
NavInputSource = ImGuiInputSource_None;
NavLayer = ImGuiNavLayer_Main;
NavIdIsAlive = false;
@@ -1880,7 +2087,7 @@ struct ImGuiContext
NavMoveForwardToNextFrame = false;
NavMoveFlags = ImGuiNavMoveFlags_None;
NavMoveScrollFlags = ImGuiScrollFlags_None;
- NavMoveKeyMods = ImGuiKeyModFlags_None;
+ NavMoveKeyMods = ImGuiModFlags_None;
NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None;
NavScoringDebugCount = 0;
NavTabbingDir = 0;
@@ -1928,6 +2135,7 @@ struct ImGuiContext
PlatformImeData.InputPos = ImVec2(0.0f, 0.0f);
PlatformImeDataPrev.InputPos = ImVec2(-1.0f, -1.0f); // Different to ensure initial submission
+ PlatformImeViewport = 0;
PlatformLocaleDecimalPoint = '.';
SettingsLoaded = false;
@@ -1973,6 +2181,7 @@ struct IMGUI_API ImGuiWindowTempData
ImVec2 PrevLineSize;
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;
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;
@@ -2012,7 +2221,12 @@ struct IMGUI_API ImGuiWindow
{
char* Name; // Window name, owned by the window.
ImGuiID ID; // == ImHashStr(Name)
- ImGuiWindowFlags Flags; // See enum ImGuiWindowFlags_
+ ImGuiWindowFlags Flags, FlagsPreviousFrame; // See enum ImGuiWindowFlags_
+ ImGuiWindowClass WindowClass; // Advanced users only. Set with SetNextWindowClass()
+ ImGuiViewportP* Viewport; // Always set in Begin(). Inactive windows may have a NULL value here if their viewport was discarded.
+ 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 (always rounded-up to nearest pixel)
ImVec2 Size; // Current size (==SizeFull or collapsed title bar size)
ImVec2 SizeFull; // Size when non collapsed
@@ -2024,6 +2238,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;
@@ -2032,6 +2247,7 @@ struct IMGUI_API ImGuiWindow
ImVec2 ScrollTargetEdgeSnapDist; // 0.0f = no snapping, >0.0f snapping threshold
ImVec2 ScrollbarSizes; // Size taken by each scrollbars on their smaller axis. Pay attention! ScrollbarSizes.x == width of the vertical scrollbar, ScrollbarSizes.y = height of the horizontal scrollbar.
bool ScrollbarX, ScrollbarY; // Are scrollbars visible?
+ 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
@@ -2060,6 +2276,7 @@ struct IMGUI_API ImGuiWindow
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.
+ ImGuiCond SetWindowDockAllowFlags : 8; // 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.
@@ -2079,11 +2296,13 @@ struct IMGUI_API ImGuiWindow
ImVec2ih HitTestHoleOffset;
int LastFrameActive; // Last frame number the window was Active.
+ int LastFrameJustFocused; // Last frame number the window was made Focused.
float LastTimeActive; // Last timestamp the window was Active (using float as we don't need high precision there)
float ItemWidthDefault;
ImGuiStorage StateStorage;
ImVector<ImGuiOldColumns> ColumnsStorage;
float FontWindowScale; // User scale multiplier per-window, via SetWindowFontScale()
+ float FontDpiScale;
int SettingsOffset; // Offset into SettingsWindows[] (offsets are always valid as we only grow the array from the back)
ImDrawList* DrawList; // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer)
@@ -2092,6 +2311,7 @@ struct IMGUI_API ImGuiWindow
ImGuiWindow* ParentWindowInBeginStack;
ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. Doesn't cross through popups/dock nodes.
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.
@@ -2103,6 +2323,19 @@ struct IMGUI_API ImGuiWindow
int MemoryDrawListVtxCapacity;
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 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.
+ 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;
+
public:
ImGuiWindow(ImGuiContext* context, const char* name);
~ImGuiWindow();
@@ -2110,14 +2343,11 @@ public:
ImGuiID GetID(const char* str, const char* str_end = NULL);
ImGuiID GetID(const void* ptr);
ImGuiID GetID(int n);
- ImGuiID GetIDNoKeepAlive(const char* str, const char* str_end = NULL);
- ImGuiID GetIDNoKeepAlive(const void* ptr);
- ImGuiID GetIDNoKeepAlive(int n);
ImGuiID GetIDFromRectangle(const ImRect& r_abs);
// 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 { ImGuiContext& g = *GImGui; float scale = g.FontBaseSize * FontWindowScale; if (ParentWindow) scale *= ParentWindow->FontWindowScale; return scale; }
+ float CalcFontSize() const { ImGuiContext& g = *GImGui; float scale = g.FontBaseSize * FontWindowScale * FontDpiScale; if (ParentWindow) scale *= ParentWindow->FontWindowScale; return scale; }
float TitleBarHeight() const { ImGuiContext& g = *GImGui; return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : CalcFontSize() + g.Style.FramePadding.y * 2.0f; }
ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); }
float MenuBarHeight() const { ImGuiContext& g = *GImGui; return (Flags & ImGuiWindowFlags_MenuBar) ? DC.MenuBarOffset.y + CalcFontSize() + g.Style.FramePadding.y * 2.0f : 0.0f; }
@@ -2141,14 +2371,17 @@ 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_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.
+ ImGuiTabItemFlags_Preview = 1 << 23 // [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() 40 bytes)
+// Storage for one active tab item (sizeof() 48 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
@@ -2201,6 +2434,8 @@ struct IMGUI_API ImGuiTabBar
int GetTabOrder(const ImGuiTabItem* tab) const { return Tabs.index_from_ptr(tab); }
const char* GetTabName(const ImGuiTabItem* tab) const
{
+ if (tab->Window)
+ return tab->Window->Name;
IM_ASSERT(tab->NameOffset != -1 && tab->NameOffset < TabsNames.Buf.Size);
return TabsNames.Buf.Data + tab->NameOffset;
}
@@ -2287,6 +2522,15 @@ struct ImGuiTableCellData
ImGuiTableColumnIdx Column; // Column number
};
+// Per-instance data that needs preserving across frames (seemingly most others do not need to be preserved aside from debug needs, does that needs they could be moved to ImGuiTableTempData ?)
+struct ImGuiTableInstanceData
+{
+ float LastOuterHeight; // Outer height from last frame // FIXME: multi-instance issue (#3955)
+ float LastFirstRowHeight; // Height of first row from last frame // FIXME: possible multi-instance issue?
+
+ ImGuiTableInstanceData() { LastOuterHeight = LastFirstRowHeight = 0.0f; }
+};
+
// FIXME-TABLE: more transient data could be stored in a per-stacked table structure: DrawSplitter, SortSpecs, incoming RowData
struct IMGUI_API ImGuiTable
{
@@ -2329,8 +2573,6 @@ struct IMGUI_API ImGuiTable
float CellPaddingY;
float CellSpacingX1; // Spacing between non-bordered cells
float CellSpacingX2;
- float LastOuterHeight; // Outer height from last frame
- float LastFirstRowHeight; // Height of first row from last frame
float InnerWidth; // User value passed to BeginTable(), see comments at the top of BeginTable() for details.
float ColumnsGivenWidth; // Sum of current column width
float ColumnsAutoFitWidth; // Sum of ideal column width in order nothing to be clipped, used for auto-fitting and content width submission in outer window
@@ -2350,6 +2592,8 @@ struct IMGUI_API ImGuiTable
ImGuiWindow* InnerWindow; // Window holding the table data (== OuterWindow or a child window)
ImGuiTextBuffer ColumnsNames; // Contiguous buffer holding columns names
ImDrawListSplitter* DrawSplitter; // Shortcut to TempData->DrawSplitter while in table. Isolate draw commands per columns to avoid switching clip rect constantly
+ ImGuiTableInstanceData InstanceDataFirst;
+ ImVector<ImGuiTableInstanceData> InstanceDataExtra; // FIXME-OPT: Using a small-vector pattern would be good.
ImGuiTableColumnSortSpecs SortSpecsSingle;
ImVector<ImGuiTableColumnSortSpecs> SortSpecsMulti; // FIXME-OPT: Using a small-vector pattern would be good.
ImGuiTableSortSpecs SortSpecs; // Public facing sorts specs, this is what we return in TableGetSortSpecs()
@@ -2477,7 +2721,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 popup_hierarchy);
+ IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy, bool dock_hierarchy);
IMGUI_API bool IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent);
IMGUI_API bool IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below);
IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window);
@@ -2501,18 +2745,17 @@ namespace ImGui
// Fonts, drawing
IMGUI_API void SetCurrentFont(ImFont* font);
inline ImFont* GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; }
- inline ImDrawList* GetForegroundDrawList(ImGuiWindow* window) { IM_UNUSED(window); return GetForegroundDrawList(); } // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'docking' branches.
- IMGUI_API ImDrawList* GetBackgroundDrawList(ImGuiViewport* viewport); // get background draw list for the given viewport. this draw list will be the first rendering one. Useful to quickly draw shapes/text behind dear imgui contents.
- IMGUI_API ImDrawList* GetForegroundDrawList(ImGuiViewport* viewport); // get foreground draw list for the given viewport. this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents.
+ inline ImDrawList* GetForegroundDrawList(ImGuiWindow* window) { return GetForegroundDrawList(window->Viewport); }
// Init
- 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().
+ IMGUI_API void Initialize();
+ IMGUI_API void Shutdown(); // Since 1.60 this is a _private_ function. You can call DestroyContext() to destroy the context created by CreateContext().
// NewFrame
IMGUI_API void UpdateInputEvents(bool trickle_fast_inputs);
IMGUI_API void UpdateHoveredWindowAndCaptureFlags();
IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window);
+ IMGUI_API void StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* node, bool undock_floating_node);
IMGUI_API void UpdateMouseMovingWindowNewFrame();
IMGUI_API void UpdateMouseMovingWindowEndFrame();
@@ -2521,6 +2764,15 @@ namespace ImGui
IMGUI_API void RemoveContextHook(ImGuiContext* context, ImGuiID hook_to_remove);
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 SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport);
+ IMGUI_API void SetCurrentViewport(ImGuiWindow* window, ImGuiViewportP* viewport);
+ IMGUI_API const ImGuiPlatformMonitor* GetViewportPlatformMonitor(ImGuiViewport* viewport);
+ IMGUI_API ImGuiViewportP* FindHoveredViewportFromPlatformWindowStack(const ImVec2& mouse_platform_pos);
+
// Settings
IMGUI_API void MarkIniSettingsDirty();
IMGUI_API void MarkIniSettingsDirty(ImGuiWindow* window);
@@ -2528,6 +2780,8 @@ namespace ImGui
IMGUI_API ImGuiWindowSettings* CreateNewWindowSettings(const char* name);
IMGUI_API ImGuiWindowSettings* FindWindowSettings(ImGuiID id);
IMGUI_API ImGuiWindowSettings* FindOrCreateWindowSettings(const char* name);
+ IMGUI_API void AddSettingsHandler(const ImGuiSettingsHandler* handler);
+ IMGUI_API void RemoveSettingsHandler(const char* type_name);
IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name);
// Scrolling
@@ -2563,7 +2817,7 @@ namespace ImGui
// Basic Helpers for widget code
IMGUI_API void ItemSize(const ImVec2& size, float text_baseline_y = -1.0f);
- IMGUI_API void ItemSize(const ImRect& bb, float text_baseline_y = -1.0f);
+ inline void ItemSize(const ImRect& bb, float text_baseline_y = -1.0f) { ItemSize(bb.GetSize(), text_baseline_y); } // FIXME: This is a misleading API since we expect CursorPos to be bb.Min.
IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL, ImGuiItemFlags extra_flags = 0);
IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id);
IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id);
@@ -2661,11 +2915,62 @@ namespace ImGui
IMGUI_API bool IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold = -1.0f);
inline bool IsNavInputDown(ImGuiNavInput n) { ImGuiContext& g = *GImGui; return g.IO.NavInputs[n] > 0.0f; }
inline bool IsNavInputTest(ImGuiNavInput n, ImGuiInputReadMode rm) { return (GetNavInputAmount(n, rm) > 0.0f); }
- IMGUI_API ImGuiKeyModFlags GetMergedKeyModFlags();
+ IMGUI_API ImGuiModFlags GetMergedModFlags();
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
- inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); }
+ inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } // [removed in 1.87]
#endif
+ // 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 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 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);
+ 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 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; }
+ IMGUI_API bool GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window);
+ IMGUI_API void BeginDocked(ImGuiWindow* window, bool* p_open);
+ 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.
+ // - 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.
+ // - 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.
+ 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 = 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_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);
+ 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<const char*>* in_window_remap_pairs);
+ IMGUI_API void DockBuilderCopyNode(ImGuiID src_node_id, ImGuiID dst_node_id, ImVector<ImGuiID>* 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);
IMGUI_API void ClearDragDrop();
@@ -2705,6 +3010,7 @@ namespace ImGui
IMGUI_API void TableDrawBorders(ImGuiTable* table);
IMGUI_API void TableDrawContextMenu(ImGuiTable* table);
IMGUI_API void TableMergeDrawChannels(ImGuiTable* table);
+ inline ImGuiTableInstanceData* TableGetInstanceData(ImGuiTable* table, int instance_no) { if (instance_no == 0) return &table->InstanceDataFirst; return &table->InstanceDataExtra[instance_no - 1]; }
IMGUI_API void TableSortSpecsSanitize(ImGuiTable* table);
IMGUI_API void TableSortSpecsBuild(ImGuiTable* table);
IMGUI_API ImGuiSortDirection TableGetColumnNextSortDirection(ImGuiTableColumn* column);
@@ -2730,19 +3036,21 @@ namespace ImGui
IMGUI_API void TableSaveSettings(ImGuiTable* table);
IMGUI_API void TableResetSettings(ImGuiTable* table);
IMGUI_API ImGuiTableSettings* TableGetBoundSettings(ImGuiTable* table);
- IMGUI_API void TableSettingsInstallHandler(ImGuiContext* context);
+ IMGUI_API void TableSettingsAddSettingsHandler();
IMGUI_API ImGuiTableSettings* TableSettingsCreate(ImGuiID id, int columns_count);
IMGUI_API ImGuiTableSettings* TableSettingsFindByID(ImGuiID id);
// Tab Bars
- 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 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);
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);
+ 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 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);
@@ -2760,27 +3068,23 @@ 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);
- IMGUI_API void RenderRectFilledWithHole(ImDrawList* draw_list, ImRect outer, ImRect inner, ImU32 col, float rounding);
-
-#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]
- inline void RenderArrow(ImVec2 pos, ImGuiDir dir, float scale=1.0f) { ImGuiWindow* window = GetCurrentWindow(); RenderArrow(window->DrawList, pos, GetColorU32(ImGuiCol_Text), dir, scale); }
- inline void RenderBullet(ImVec2 pos) { ImGuiWindow* window = GetCurrentWindow(); RenderBullet(window->DrawList, pos, GetColorU32(ImGuiCol_Text)); }
-#endif
+ IMGUI_API void RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer, const ImRect& inner, ImU32 col, float rounding);
+ IMGUI_API ImDrawFlags CalcRoundingFlagsForRectInRect(const ImRect& r_in, const ImRect& r_outer, float threshold);
// Widgets
IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0);
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);
- 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 = 0);
IMGUI_API void Scrollbar(ImGuiAxis axis);
IMGUI_API bool ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 avail_v, ImS64 contents_v, ImDrawFlags flags);
@@ -2797,7 +3101,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);
@@ -2809,7 +3113,7 @@ namespace ImGui
template<typename T, typename SIGNED_T, typename FLOAT_T> IMGUI_API T ScaleValueFromRatioT(ImGuiDataType data_type, float t, T v_min, T v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_size);
template<typename T, typename SIGNED_T, typename FLOAT_T> IMGUI_API bool DragBehaviorT(ImGuiDataType data_type, T* v, float v_speed, T v_min, T v_max, const char* format, ImGuiSliderFlags flags);
template<typename T, typename SIGNED_T, typename FLOAT_T> IMGUI_API bool SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, T* v, T v_min, T v_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb);
- template<typename T, typename SIGNED_T> IMGUI_API T RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v);
+ template<typename T> IMGUI_API T RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v);
template<typename T> IMGUI_API bool CheckboxFlagsT(const char* label, T* flags, T flags_value);
// Data type helpers
@@ -2853,7 +3157,8 @@ namespace ImGui
IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas);
IMGUI_API void DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end);
IMGUI_API void DebugNodeColumns(ImGuiOldColumns* columns);
- IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, const char* label);
+ IMGUI_API void DebugNodeDockNode(ImGuiDockNode* node, const char* label);
+ IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label);
IMGUI_API void DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb);
IMGUI_API void DebugNodeFont(ImFont* font);
IMGUI_API void DebugNodeStorage(ImGuiStorage* storage, const char* label);
diff --git a/3rdparty/imgui/source/imgui_tables.cpp b/3rdparty/imgui/source/imgui_tables.cpp
index 27f1d53..9a01c69 100644
--- a/3rdparty/imgui/source/imgui_tables.cpp
+++ b/3rdparty/imgui/source/imgui_tables.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.87
+// dear imgui, v1.88 WIP
// (tables and columns code)
/*
@@ -24,7 +24,7 @@ Index of this file:
*/
// Navigating this file:
-// - In Visual Studio IDE: CTRL+comma ("Edit.NavigateTo") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
+// - In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
// - With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments.
//-----------------------------------------------------------------------------
@@ -361,6 +361,8 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
table->IsLayoutLocked = false;
table->InnerWidth = inner_width;
temp_data->UserOuterSize = outer_size;
+ if (instance_no > 0 && table->InstanceDataExtra.Size < instance_no)
+ table->InstanceDataExtra.push_back(ImGuiTableInstanceData());
// When not using a child window, WorkRect.Max will grow as we append contents.
if (use_child_window)
@@ -933,9 +935,10 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
width_remaining_for_stretched_columns -= 1.0f;
}
+ ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
table->HoveredColumnBody = -1;
table->HoveredColumnBorder = -1;
- const ImRect mouse_hit_rect(table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.Max.x, ImMax(table->OuterRect.Max.y, table->OuterRect.Min.y + table->LastOuterHeight));
+ const ImRect mouse_hit_rect(table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.Max.x, ImMax(table->OuterRect.Max.y, table->OuterRect.Min.y + table_instance->LastOuterHeight));
const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0);
// [Part 6] Setup final position, offset, skip/clip states and clipping rectangles, detect hovered column
@@ -1096,7 +1099,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
// [Part 10] Hit testing on borders
if (table->Flags & ImGuiTableFlags_Resizable)
TableUpdateBorders(table);
- table->LastFirstRowHeight = 0.0f;
+ table_instance->LastFirstRowHeight = 0.0f;
table->IsLayoutLocked = true;
table->IsUsingHeaders = false;
@@ -1141,10 +1144,11 @@ void ImGui::TableUpdateBorders(ImGuiTable* table)
// use the final height from last frame. Because this is only affecting _interaction_ with columns, it is not
// really problematic (whereas the actual visual will be displayed in EndTable() and using the current frame height).
// Actual columns highlight/render will be performed in EndTable() and not be affected.
+ ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
const float hit_half_width = TABLE_RESIZE_SEPARATOR_HALF_THICKNESS;
const float hit_y1 = table->OuterRect.Min.y;
- const float hit_y2_body = ImMax(table->OuterRect.Max.y, hit_y1 + table->LastOuterHeight);
- const float hit_y2_head = hit_y1 + table->LastFirstRowHeight;
+ const float hit_y2_body = ImMax(table->OuterRect.Max.y, hit_y1 + table_instance->LastOuterHeight);
+ const float hit_y2_head = hit_y1 + table_instance->LastFirstRowHeight;
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
{
@@ -1223,6 +1227,7 @@ void ImGui::EndTable()
TableOpenContextMenu((int)table->HoveredColumnBody);
// Finalize table height
+ ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
inner_window->DC.PrevLineSize = temp_data->HostBackupPrevLineSize;
inner_window->DC.CurrLineSize = temp_data->HostBackupCurrLineSize;
inner_window->DC.CursorMaxPos = temp_data->HostBackupCursorMaxPos;
@@ -1233,7 +1238,7 @@ void ImGui::EndTable()
else if (!(flags & ImGuiTableFlags_NoHostExtendY))
table->OuterRect.Max.y = table->InnerRect.Max.y = ImMax(table->OuterRect.Max.y, inner_content_max_y); // Patch OuterRect/InnerRect height
table->WorkRect.Max.y = ImMax(table->WorkRect.Max.y, table->OuterRect.Max.y);
- table->LastOuterHeight = table->OuterRect.GetHeight();
+ table_instance->LastOuterHeight = table->OuterRect.GetHeight();
// Setup inner scrolling range
// FIXME: This ideally should be done earlier, in BeginTable() SetNextWindowContentSize call, just like writing to inner_window->DC.CursorMaxPos.y,
@@ -1749,7 +1754,7 @@ void ImGui::TableEndRow(ImGuiTable* table)
const bool unfreeze_rows_actual = (table->CurrentRow + 1 == table->FreezeRowsCount);
const bool unfreeze_rows_request = (table->CurrentRow + 1 == table->FreezeRowsRequest);
if (table->CurrentRow == 0)
- table->LastFirstRowHeight = bg_y2 - bg_y1;
+ TableGetInstanceData(table, table->InstanceCurrent)->LastFirstRowHeight = bg_y2 - bg_y1;
const bool is_visible = (bg_y2 >= table->InnerClipRect.Min.y && bg_y1 <= table->InnerClipRect.Max.y);
if (is_visible)
@@ -2502,10 +2507,11 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
inner_drawlist->PushClipRect(table->Bg0ClipRectForDrawCmd.Min, table->Bg0ClipRectForDrawCmd.Max, false);
// Draw inner border and resizing feedback
+ ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
const float border_size = TABLE_BORDER_SIZE;
const float draw_y1 = table->InnerRect.Min.y;
const float draw_y2_body = table->InnerRect.Max.y;
- const float draw_y2_head = table->IsUsingHeaders ? ImMin(table->InnerRect.Max.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table->LastFirstRowHeight) : draw_y1;
+ const float draw_y2_head = table->IsUsingHeaders ? ImMin(table->InnerRect.Max.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table_instance->LastFirstRowHeight) : draw_y1;
if (table->Flags & ImGuiTableFlags_BordersInnerV)
{
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
@@ -3427,9 +3433,8 @@ static void TableSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandle
}
}
-void ImGui::TableSettingsInstallHandler(ImGuiContext* context)
+void ImGui::TableSettingsAddSettingsHandler()
{
- ImGuiContext& g = *context;
ImGuiSettingsHandler ini_handler;
ini_handler.TypeName = "Table";
ini_handler.TypeHash = ImHashStr("Table");
@@ -3438,7 +3443,7 @@ void ImGui::TableSettingsInstallHandler(ImGuiContext* context)
ini_handler.ReadLineFn = TableSettingsHandler_ReadLine;
ini_handler.ApplyAllFn = TableSettingsHandler_ApplyAll;
ini_handler.WriteAllFn = TableSettingsHandler_WriteAll;
- g.SettingsHandlers.push_back(ini_handler);
+ AddSettingsHandler(&ini_handler);
}
//-------------------------------------------------------------------------
@@ -3536,6 +3541,8 @@ void ImGui::DebugNodeTable(ImGuiTable* table)
GetForegroundDrawList()->AddRect(GetItemRectMin(), GetItemRectMax(), IM_COL32(255, 255, 0, 255));
if (!open)
return;
+ if (table->InstanceCurrent > 0)
+ ImGui::Text("** %d instances of same table! Some data below will refer to last instance.", table->InstanceCurrent + 1);
bool clear_settings = SmallButton("Clear settings");
BulletText("OuterRect: Pos: (%.1f,%.1f) Size: (%.1f,%.1f) Sizing: '%s'", table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.GetWidth(), table->OuterRect.GetHeight(), DebugNodeTableGetSizingPolicyDesc(table->Flags));
BulletText("ColumnsGivenWidth: %.1f, ColumnsAutoFitWidth: %.1f, InnerWidth: %.1f%s", table->ColumnsGivenWidth, table->ColumnsAutoFitWidth, table->InnerWidth, table->InnerWidth == 0.0f ? " (auto)" : "");
diff --git a/3rdparty/imgui/source/imgui_widgets.cpp b/3rdparty/imgui/source/imgui_widgets.cpp
index 56723cd..cf2baf1 100644
--- a/3rdparty/imgui/source/imgui_widgets.cpp
+++ b/3rdparty/imgui/source/imgui_widgets.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.87
+// dear imgui, v1.88 WIP
// (widgets code)
/*
@@ -82,6 +82,7 @@ Index of this file:
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
+#pragma GCC diagnostic ignored "-Wdeprecated-enum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
#endif
//-------------------------------------------------------------------------
@@ -166,7 +167,21 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags)
const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
const float wrap_pos_x = window->DC.TextWrapPos;
const bool wrap_enabled = (wrap_pos_x >= 0.0f);
- if (text_end - text > 2000 && !wrap_enabled)
+ if (text_end - text <= 2000 || wrap_enabled)
+ {
+ // Common case
+ 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);
+
+ ImRect bb(text_pos, text_pos + text_size);
+ ItemSize(text_size, 0.0f);
+ 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);
+ }
+ else
{
// Long text!
// Perform manual coarse clipping to optimize for long multi-line text
@@ -239,19 +254,6 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags)
ItemSize(text_size, 0.0f);
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);
-
- ImRect bb(text_pos, text_pos + text_size);
- ItemSize(text_size, 0.0f);
- 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::TextUnformatted(const char* text, const char* text_end)
@@ -502,7 +504,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;
+ const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindowDockTree == window->RootWindowDockTree;
if (flatten_hovered_children)
g.HoveredWindow = window;
@@ -829,7 +831,8 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos)
return pressed;
}
-bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos)
+// The Collapse button also functions as a Dock Menu button.
+bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos, ImGuiDockNode* dock_node)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
@@ -840,23 +843,27 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos)
bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None);
// Render
+ //bool is_dock_menu = (window->DockNodeAsHost && !window->Collapsed);
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/*+ ImVec2(0.0f, -0.5f)*/, g.FontSize * 0.5f + 1.0f, bg_col, 12);
- RenderArrow(window->DrawList, bb.Min + g.Style.FramePadding, text_col, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f);
+ window->DrawList->AddCircleFilled(bb.GetCenter() + 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);
+ else
+ RenderArrow(window->DrawList, bb.Min + g.Style.FramePadding, text_col, 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(0))
- StartMouseMovingWindow(window);
+ StartMouseMovingWindowOrNode(window, dock_node, true);
return pressed;
}
ImGuiID ImGui::GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis)
{
- return window->GetIDNoKeepAlive(axis == ImGuiAxis_X ? "#SCROLLX" : "#SCROLLY");
+ return window->GetID(axis == ImGuiAxis_X ? "#SCROLLX" : "#SCROLLY");
}
// Return scrollbar rectangle, must only be called for corresponding axis if window->ScrollbarX/Y is set.
@@ -1285,7 +1292,7 @@ void ImGui::Bullet()
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
- const float line_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + g.Style.FramePadding.y * 2), g.FontSize);
+ const float line_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + 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))
@@ -1341,6 +1348,7 @@ void ImGui::NewLine()
ImGuiContext& g = *GImGui;
const ImGuiLayoutType backup_layout_type = window->DC.LayoutType;
window->DC.LayoutType = ImGuiLayoutType_Vertical;
+ window->DC.IsSameLine = false;
if (window->DC.CurrLineSize.y > 0.0f) // In the event that we are on a line with items that is smaller that FontSize high, we will preserve its height.
ItemSize(ImVec2(0, 0));
else
@@ -1443,7 +1451,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;
@@ -1495,7 +1503,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);
@@ -1733,6 +1743,7 @@ bool ImGui::BeginComboPreview()
window->DC.CursorPos = preview_data->PreviewRect.Min + g.Style.FramePadding;
window->DC.CursorMaxPos = window->DC.CursorPos;
window->DC.LayoutType = ImGuiLayoutType_Horizontal;
+ window->DC.IsSameLine = false;
PushClipRect(preview_data->PreviewRect.Min, preview_data->PreviewRect.Max, true);
return true;
@@ -1758,6 +1769,7 @@ void ImGui::EndComboPreview()
window->DC.CursorPosPrevLine = preview_data->BackupCursorPosPrevLine;
window->DC.PrevLineTextBaseOffset = preview_data->BackupPrevLineTextBaseOffset;
window->DC.LayoutType = preview_data->BackupLayout;
+ window->DC.IsSameLine = false;
preview_data->PreviewRect = ImRect();
}
@@ -2007,23 +2019,20 @@ bool ImGui::DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void
ImGuiDataTypeTempStorage data_backup;
memcpy(&data_backup, p_data, type_info->Size);
- if (format == NULL)
+ // Sanitize format
+ // For float/double we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in, so force them into %f and %lf
+ char format_sanitized[32];
+ if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double)
format = type_info->ScanFmt;
-
- if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32 || data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64 || data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double)
- {
- // For float/double we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in, so force them into %f and %lf
- if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double)
- format = type_info->ScanFmt;
- if (sscanf(buf, format, p_data) < 1)
- return false;
- }
else
+ format = ImParseFormatSanitizeForScanning(format, format_sanitized, IM_ARRAYSIZE(format_sanitized));
+
+ // Small types need a 32-bit buffer to receive the result from scanf()
+ int v32 = 0;
+ if (sscanf(buf, format, type_info->Size >= 4 ? p_data : &v32) < 1)
+ return false;
+ if (type_info->Size < 4)
{
- // Small types need a 32-bit buffer to receive the result from scanf()
- int v32;
- if (sscanf(buf, format, &v32) < 1)
- return false;
if (data_type == ImGuiDataType_S8)
*(ImS8*)p_data = (ImS8)ImClamp(v32, (int)IM_S8_MIN, (int)IM_S8_MAX);
else if (data_type == ImGuiDataType_U8)
@@ -2105,45 +2114,17 @@ static float GetMinimumStepAtDecimalPrecision(int decimal_precision)
}
template<typename TYPE>
-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;
-}
-
-// Sanitize format
-// - Zero terminate so extra characters after format (e.g. "%f123") don't confuse atof/atoi
-// - stb_sprintf.h supports several new modifiers which format numbers in a way that also makes them incompatible atof/atoi.
-static void SanitizeFormatString(const char* fmt, char* fmt_out, size_t fmt_out_size)
-{
- IM_UNUSED(fmt_out_size);
- const char* fmt_end = ImParseFormatFindEnd(fmt);
- IM_ASSERT((size_t)(fmt_end - fmt + 1) < fmt_out_size); // Format is too long, let us know if this happens to you!
- while (fmt < fmt_end)
- {
- char c = *(fmt++);
- if (c != '\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '.
- *(fmt_out++) = c;
- }
- *fmt_out = 0; // Zero-terminate
-}
-
-template<typename TYPE, typename SIGNEDTYPE>
TYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, TYPE v)
{
+ IM_UNUSED(data_type);
+ IM_ASSERT(data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double);
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;
// Sanitize format
char fmt_sanitized[32];
- SanitizeFormatString(fmt_start, fmt_sanitized, IM_ARRAYSIZE(fmt_sanitized));
+ ImParseFormatSanitizeForPrinting(fmt_start, fmt_sanitized, IM_ARRAYSIZE(fmt_sanitized));
fmt_start = fmt_sanitized;
// Format value with our rounding, and read back
@@ -2152,10 +2133,8 @@ TYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type,
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);
+ v = (TYPE)ImAtof(p);
+
return v;
}
@@ -2259,8 +2238,8 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const
}
// Round to user desired precision based on format string
- if (!(flags & ImGuiSliderFlags_NoRoundToFormat))
- v_cur = RoundScalarWithFormatT<TYPE, SIGNEDTYPE>(format, data_type, v_cur);
+ if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat))
+ v_cur = RoundScalarWithFormatT<TYPE>(format, data_type, v_cur);
// Preserve remainder after rounding has been applied. This also allow slow tweaking of values.
g.DragCurrentAccumDirty = false;
@@ -2856,8 +2835,8 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ
// Calculate what our "new" clicked_t will be, and thus how far we actually moved the slider, and subtract this from the accumulator
TYPE v_new = ScaleValueFromRatioT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
- if (!(flags & ImGuiSliderFlags_NoRoundToFormat))
- v_new = RoundScalarWithFormatT<TYPE, SIGNEDTYPE>(format, data_type, v_new);
+ if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat))
+ v_new = RoundScalarWithFormatT<TYPE>(format, data_type, v_new);
float new_clicked_t = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, v_new, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
if (delta > 0)
@@ -2875,8 +2854,8 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ
TYPE v_new = ScaleValueFromRatioT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
// Round to user desired precision based on format string
- if (!(flags & ImGuiSliderFlags_NoRoundToFormat))
- v_new = RoundScalarWithFormatT<TYPE, SIGNEDTYPE>(format, data_type, v_new);
+ if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat))
+ v_new = RoundScalarWithFormatT<TYPE>(format, data_type, v_new);
// Apply result
if (*v != v_new)
@@ -3219,6 +3198,8 @@ bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, i
// - ImParseFormatFindStart() [Internal]
// - ImParseFormatFindEnd() [Internal]
// - ImParseFormatTrimDecorations() [Internal]
+// - ImParseFormatSanitizeForPrinting() [Internal]
+// - ImParseFormatSanitizeForScanning() [Internal]
// - ImParseFormatPrecision() [Internal]
// - TempInputTextScalar() [Internal]
// - InputScalar()
@@ -3282,6 +3263,57 @@ const char* ImParseFormatTrimDecorations(const char* fmt, char* buf, size_t buf_
return buf;
}
+// Sanitize format
+// - Zero terminate so extra characters after format (e.g. "%f123") don't confuse atof/atoi
+// - stb_sprintf.h supports several new modifiers which format numbers in a way that also makes them incompatible atof/atoi.
+void ImParseFormatSanitizeForPrinting(const char* fmt_in, char* fmt_out, size_t fmt_out_size)
+{
+ const char* fmt_end = ImParseFormatFindEnd(fmt_in);
+ IM_UNUSED(fmt_out_size);
+ IM_ASSERT((size_t)(fmt_end - fmt_in + 1) < fmt_out_size); // Format is too long, let us know if this happens to you!
+ while (fmt_in < fmt_end)
+ {
+ char c = *fmt_in++;
+ if (c != '\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '.
+ *(fmt_out++) = c;
+ }
+ *fmt_out = 0; // Zero-terminate
+}
+
+// - For scanning we need to remove all width and precision fields "%3.7f" -> "%f". BUT don't strip types like "%I64d" which includes digits. ! "%07I64d" -> "%I64d"
+const char* ImParseFormatSanitizeForScanning(const char* fmt_in, char* fmt_out, size_t fmt_out_size)
+{
+ const char* fmt_end = ImParseFormatFindEnd(fmt_in);
+ const char* fmt_out_begin = fmt_out;
+ IM_UNUSED(fmt_out_size);
+ IM_ASSERT((size_t)(fmt_end - fmt_in + 1) < fmt_out_size); // Format is too long, let us know if this happens to you!
+ bool has_type = false;
+ while (fmt_in < fmt_end)
+ {
+ char c = *fmt_in++;
+ if (!has_type && ((c >= '0' && c <= '9') || c == '.'))
+ continue;
+ has_type |= ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); // Stop skipping digits
+ if (c != '\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '.
+ *(fmt_out++) = c;
+ }
+ *fmt_out = 0; // Zero-terminate
+ return fmt_out_begin;
+}
+
+template<typename TYPE>
+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;
+}
+
// 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)
@@ -3328,6 +3360,14 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char*
return value_changed;
}
+static inline ImGuiInputTextFlags InputScalar_DefaultCharsFilter(ImGuiDataType data_type, const char* format)
+{
+ if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double)
+ return ImGuiInputTextFlags_CharsScientific;
+ const char format_last_char = format[0] ? format[strlen(format) - 1] : 0;
+ return (format_last_char == 'x' || format_last_char == 'X') ? ImGuiInputTextFlags_CharsHexadecimal : ImGuiInputTextFlags_CharsDecimal;
+}
+
// Note that Drag/Slider functions are only forwarding the min/max values clamping values if the ImGuiSliderFlags_AlwaysClamp flag is set!
// This is intended: this way we allow CTRL+Click manual input to set a value out of bounds, for maximum flexibility.
// However this may not be ideal for all uses, as some user code may break on out of bound values.
@@ -3340,7 +3380,8 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG
ImStrTrimBlanks(data_buf);
ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoMarkEdited;
- flags |= ((data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) ? ImGuiInputTextFlags_CharsScientific : ImGuiInputTextFlags_CharsDecimal);
+ flags |= InputScalar_DefaultCharsFilter(data_type, format);
+
bool value_changed = false;
if (TempInputText(bb, id, label, data_buf, IM_ARRAYSIZE(data_buf), flags))
{
@@ -3350,7 +3391,7 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG
memcpy(&data_backup, p_data, data_type_size);
// Apply new value (or operations) then clamp
- DataTypeApplyFromText(data_buf, data_type, p_data, NULL);
+ DataTypeApplyFromText(data_buf, data_type, p_data, format);
if (p_clamp_min || p_clamp_max)
{
if (p_clamp_min && p_clamp_max && DataTypeCompare(data_type, p_clamp_min, p_clamp_max) > 0)
@@ -3383,12 +3424,12 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data
char buf[64];
DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format);
- bool value_changed = false;
- if ((flags & (ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0)
- flags |= ImGuiInputTextFlags_CharsDecimal;
- flags |= ImGuiInputTextFlags_AutoSelectAll;
- flags |= ImGuiInputTextFlags_NoMarkEdited; // We call MarkItemEdited() ourselves by comparing the actual data rather than the string.
+ // Testing ActiveId as a minor optimization as filtering is not needed until active
+ if (g.ActiveId == 0 && (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0)
+ flags |= InputScalar_DefaultCharsFilter(data_type, format);
+ flags |= ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoMarkEdited; // We call MarkItemEdited() ourselves by comparing the actual data rather than the string.
+ bool value_changed = false;
if (p_step != NULL)
{
const float button_size = GetFrameHeight();
@@ -3532,6 +3573,9 @@ bool ImGui::InputDouble(const char* label, double* v, double step, double step_f
// - InputText()
// - InputTextWithHint()
// - InputTextMultiline()
+// - InputTextGetCharInfo() [Internal]
+// - InputTextReindexLines() [Internal]
+// - InputTextReindexLinesRange() [Internal]
// - InputTextEx() [Internal]
//-------------------------------------------------------------------------
@@ -3812,7 +3856,7 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f
if (c < 0x20)
{
bool pass = false;
- pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline));
+ pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline)); // Note that an Enter KEY will emit \r and be ignored (we poll for KEY in InputText() code)
pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput));
if (!pass)
return false;
@@ -4198,16 +4242,15 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (state->SelectedAllMouseLock && !io.MouseDown[0])
state->SelectedAllMouseLock = false;
- // It is ill-defined whether the backend needs to send a \t character when pressing the TAB keys.
- // Win32 and GLFW naturally do it but not SDL.
+ // We except backends to emit a Tab key but some also emit a Tab character which we ignore (#2467, #1336)
+ // (For Tab and Enter: Win32/SFML/Allegro are sending both keys and chars, GLFW and SDL are only sending keys. For Space they all send all threes)
const bool ignore_char_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper);
if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressed(ImGuiKey_Tab) && !ignore_char_inputs && !io.KeyShift && !is_readonly)
- if (!io.InputQueueCharacters.contains('\t'))
- {
- unsigned int c = '\t'; // Insert TAB
- if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard))
- state->OnKeyPressed((int)c);
- }
+ {
+ unsigned int c = '\t'; // Insert TAB
+ if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard))
+ state->OnKeyPressed((int)c);
+ }
// Process regular 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.
@@ -4218,7 +4261,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
{
// Insert character if they pass filtering
unsigned int c = (unsigned int)io.InputQueueCharacters[n];
- if (c == '\t' && io.KeyShift)
+ if (c == '\t') // Skip Tab, see above.
continue;
if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard))
state->OnKeyPressed((int)c);
@@ -4234,19 +4277,18 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id)
{
IM_ASSERT(state != NULL);
- IM_ASSERT(io.KeyMods == GetMergedKeyModFlags() && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods"); // We rarely do this check, but if anything let's do it here.
const int row_count_per_page = ImMax((int)((inner_size.y - style.FramePadding.y) / g.FontSize), 1);
state->Stb.row_count_per_page = row_count_per_page;
const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0);
const bool is_osx = io.ConfigMacOSXBehaviors;
- const bool is_osx_shift_shortcut = is_osx && (io.KeyMods == (ImGuiKeyModFlags_Super | ImGuiKeyModFlags_Shift));
+ const bool is_osx_shift_shortcut = is_osx && (io.KeyMods == (ImGuiModFlags_Super | ImGuiModFlags_Shift));
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.KeyMods == ImGuiKeyModFlags_Ctrl);
- const bool is_shift_key_only = (io.KeyMods == ImGuiKeyModFlags_Shift);
- const bool is_shortcut_key = g.IO.ConfigMacOSXBehaviors ? (io.KeyMods == ImGuiKeyModFlags_Super) : (io.KeyMods == ImGuiKeyModFlags_Ctrl);
+ const bool is_ctrl_key_only = (io.KeyMods == ImGuiModFlags_Ctrl);
+ const bool is_shift_key_only = (io.KeyMods == ImGuiModFlags_Shift);
+ const bool is_shortcut_key = g.IO.ConfigMacOSXBehaviors ? (io.KeyMods == ImGuiModFlags_Super) : (io.KeyMods == ImGuiModFlags_Ctrl);
const bool is_cut = ((is_shortcut_key && IsKeyPressed(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressed(ImGuiKey_Delete))) && !is_readonly && !is_password && (!is_multiline || state->HasSelection());
const bool is_copy = ((is_shortcut_key && IsKeyPressed(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressed(ImGuiKey_Insert))) && !is_password && (!is_multiline || state->HasSelection());
@@ -4714,6 +4756,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
g.PlatformImeData.WantVisible = true;
g.PlatformImeData.InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize);
g.PlatformImeData.InputLineHeight = g.FontSize;
+ g.PlatformImeViewport = window->Viewport->ID;
}
}
}
@@ -4996,8 +5039,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel))
{
- const float text_offset_x = (flags & ImGuiColorEditFlags_NoInputs) ? w_button : w_full + style.ItemInnerSpacing.x;
- window->DC.CursorPos = ImVec2(pos.x + text_offset_x, pos.y + style.FramePadding.y);
+ SameLine(0.0f, style.ItemInnerSpacing.x);
TextEx(label, label_display_end);
}
@@ -5453,7 +5495,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
// 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.
// Note that 'col' may be encoded in HSV if ImGuiColorEditFlags_InputHSV is set.
-bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, ImVec2 size)
+bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, const ImVec2& size_arg)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
@@ -5461,11 +5503,8 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl
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 float default_size = GetFrameHeight();
+ const ImVec2 size(size_arg.x == 0.0f ? default_size : size_arg.x, size_arg.y == 0.0f ? default_size : size_arg.y);
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))
@@ -6703,6 +6742,7 @@ bool ImGui::BeginMenuBar()
// We overwrite CursorMaxPos because BeginGroup sets it to CursorPos (essentially the .EmitItem hack in EndMenuBar() would need something analogous here, maybe a BeginGroupEx() with flags).
window->DC.CursorPos = window->DC.CursorMaxPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffset.x, bar_rect.Min.y + window->DC.MenuBarOffset.y);
window->DC.LayoutType = ImGuiLayoutType_Horizontal;
+ window->DC.IsSameLine = false;
window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
window->DC.MenuBarAppending = true;
AlignTextToFramePadding();
@@ -6746,6 +6786,7 @@ void ImGui::EndMenuBar()
g.GroupStack.back().EmitItem = false;
EndGroup(); // Restore position on layer 0
window->DC.LayoutType = ImGuiLayoutType_Vertical;
+ window->DC.IsSameLine = false;
window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
window->DC.MenuBarAppending = false;
}
@@ -6758,10 +6799,10 @@ bool ImGui::BeginViewportSideBar(const char* name, ImGuiViewport* viewport_p, Im
IM_ASSERT(dir != ImGuiDir_None);
ImGuiWindow* bar_window = FindWindowByName(name);
+ ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)(viewport_p ? viewport_p : GetMainViewport());
if (bar_window == NULL || bar_window->BeginCount == 0)
{
// Calculate and set window size/position
- ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)(viewport_p ? viewport_p : GetMainViewport());
ImRect avail_rect = viewport->GetBuildWorkRect();
ImGuiAxis axis = (dir == ImGuiDir_Up || dir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;
ImVec2 pos = avail_rect.Min;
@@ -6779,7 +6820,8 @@ bool ImGui::BeginViewportSideBar(const char* name, ImGuiViewport* viewport_p, Im
viewport->BuildWorkOffsetMax[axis] -= axis_size;
}
- window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
+ window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking;
+ 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
bool is_open = Begin(name, NULL, window_flags);
@@ -6793,6 +6835,9 @@ bool ImGui::BeginMainMenuBar()
ImGuiContext& g = *GImGui;
ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport();
+ // Notify of viewport change so GetFrameHeight() can be accurate in case of DPI change
+ SetCurrentViewport(NULL, viewport);
+
// For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set.
// FIXME: This could be generalized as an opt-in way to clamp window->DC.CursorStartPos to avoid SafeArea?
// FIXME: Consider removing support for safe area down the line... it's messy. Nowadays consoles have support for TV calibration in OS settings.
@@ -6924,7 +6969,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
if (!enabled)
EndDisabled();
- const bool hovered = (g.HoveredId == id) && enabled;
+ const bool hovered = (g.HoveredId == id) && enabled && !g.NavDisableMouseHover;
if (menuset_is_open)
g.NavWindow = backed_nav_window;
@@ -6934,7 +6979,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
{
// Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu
// Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.
- bool moving_toward_other_child_menu = false;
+ bool moving_toward_child_menu = false;
ImGuiWindow* child_menu_window = (g.BeginPopupStack.Size < g.OpenPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].SourceWindow == window) ? g.OpenPopupStack[g.BeginPopupStack.Size].Window : NULL;
if (g.HoveredWindow == window && child_menu_window != NULL && !(window->Flags & ImGuiWindowFlags_MenuBar))
{
@@ -6945,18 +6990,22 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
ImVec2 tc = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR();
float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, ref_unit * 0.5f, ref_unit * 2.5f); // add a bit of extra slack.
ta.x += (window->Pos.x < child_menu_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues (FIXME: ??)
- tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -ref_unit * 8.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale?
+ tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -ref_unit * 8.0f); // triangle has maximum height to limit the slope and the bias toward large sub-menus
tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +ref_unit * 8.0f);
- moving_toward_other_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos);
+ moving_toward_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos);
//GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_toward_other_child_menu ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG]
}
- if (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_toward_other_child_menu)
+
+ // The 'HovereWindow == window' check creates an inconsistency (e.g. moving away from menu slowly tends to hit same window, whereas moving away fast does not)
+ // But we also need to not close the top-menu menu when moving over void. Perhaps we should extend the triangle check to a larger polygon.
+ // (Remember to test this on BeginPopup("A")->BeginMenu("B") sequence which behaves slightly differently as B isn't a Child of A and hovering isn't shared.)
+ if (menu_is_open && !hovered && g.HoveredWindow == window && !moving_toward_child_menu)
want_close = true;
// Open
if (!menu_is_open && pressed) // Click/activate to open
want_open = true;
- else if (!menu_is_open && hovered && !moving_toward_other_child_menu) // Hover to open
+ else if (!menu_is_open && hovered && !moving_toward_child_menu) // Hover to open
want_open = true;
if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open
{
@@ -7136,6 +7185,7 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected,
// - TabBarCalcTabID() [Internal]
// - TabBarCalcMaxTabWidth() [Internal]
// - TabBarFindTabById() [Internal]
+// - TabBarAddTab() [Internal]
// - TabBarRemoveTab() [Internal]
// - TabBarCloseTab() [Internal]
// - TabBarScrollClamp() [Internal]
@@ -7157,7 +7207,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, ImGuiID tab_id, ImGuiTabBarSection* sections);
@@ -7220,10 +7270,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->WorkRect.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;
@@ -7248,7 +7298,8 @@ 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)))
- ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder);
+ if ((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;
// Flags
@@ -7273,6 +7324,13 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG
// Draw separator
const ImU32 col = GetColorU32((flags & ImGuiTabBarFlags_IsFocused) ? ImGuiCol_TabActive : ImGuiCol_TabUnfocusedActive);
const float y = tab_bar->BarRect.Max.y - 1.0f;
+ if (dock_node != NULL)
+ {
+ 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
{
const float separator_min_x = tab_bar->BarRect.Min.x - IM_FLOOR(window->WindowPadding.x * 0.5f);
const float separator_max_x = tab_bar->BarRect.Max.x + IM_FLOOR(window->WindowPadding.x * 0.5f);
@@ -7423,7 +7481,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->Flags & ImGuiTabItemFlags_NoCloseButton) ? false : true;
+ const bool has_close_button = (tab->Flags & ImGuiTabItemFlags_NoCloseButton) == 0;
tab->ContentWidth = TabItemCalcSize(tab_name, has_close_button).x;
int section_n = TabItemGetSectionIdx(tab);
@@ -7502,6 +7560,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);
@@ -7509,6 +7568,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;
@@ -7519,6 +7581,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_to_tab_id = g.NavWindowingTarget->ID;
+
// Update scrolling
if (scroll_to_tab_id != 0)
TabBarScrollToTab(tab_bar, scroll_to_tab_id, sections);
@@ -7540,10 +7606,6 @@ 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;
@@ -7551,12 +7613,14 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, tab_bar->BarRect.Min.x + tab_bar->WidthAllTabsIdeal);
}
-// 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)
{
- ImGuiID id = ImHashStr(label);
+ IM_UNUSED(tab_bar);
+ IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_DockNode);
+ ImGuiID id = docked_window->TabId;
KeepAliveID(id);
return id;
}
@@ -7582,6 +7646,41 @@ 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)
+{
+ ImGuiContext& g = *GImGui;
+ 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->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)
+ new_tab.LastFrameVisible = g.FrameCount - 1;
+ new_tab.Window = window; // Required so tab bar layout can compute the tab width before tab submission
+ 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.
void ImGui::TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id)
{
@@ -7856,9 +7955,9 @@ 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);
+ bool ret = TabItemEx(tab_bar, label, p_open, flags, NULL);
if (ret && !(flags & ImGuiTabItemFlags_NoPushId))
{
ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx];
@@ -7899,10 +7998,10 @@ bool ImGui::TabItemButton(const char* label, ImGuiTabItemFlags flags)
IM_ASSERT_USER_ERROR(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!");
return false;
}
- return TabItemEx(tab_bar, label, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder);
+ return TabItemEx(tab_bar, label, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder, NULL);
}
-bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags)
+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)
@@ -7914,7 +8013,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.
@@ -7959,10 +8058,21 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
const bool is_tab_button = (flags & ImGuiTabItemFlags_Button) != 0;
tab->LastFrameVisible = g.FrameCount;
tab->Flags = flags;
+ tab->Window = docked_window;
// Append name with zero-terminator
- tab->NameOffset = (ImS32)tab_bar->TabsNames.size();
- tab_bar->TabsNames.append(label, label + strlen(label) + 1);
+ // (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_bar->Flags & ImGuiTabBarFlags_DockNode);
+ tab->NameOffset = -1;
+ }
+ else
+ {
+ IM_ASSERT(tab->Window == NULL);
+ tab->NameOffset = (ImS32)tab_bar->TabsNames.size();
+ tab_bar->TabsNames.append(label, label + strlen(label) + 1); // Append name _with_ the zero-terminator.
+ }
// Update selected tab
if (tab_appearing && (tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs) && tab_bar->NextSelectedTabId == 0)
@@ -7980,7 +8090,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
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)
+ 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;
@@ -8029,32 +8139,84 @@ 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)
+ 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);
if (pressed && !is_tab_button)
tab_bar->NextSelectedTabId = 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 (g.ActiveId != id)
SetItemAllowOverlap();
- // Drag and drop: re-order tabs
- if (held && !tab_appearing && IsMouseDragging(0))
+ // Drag and drop a single floating window node moves it
+ ImGuiDockNode* node = docked_window ? docked_window->DockNode : NULL;
+ const bool single_floating_window_node = node && node->IsFloatingNode() && (node->Windows.Size == 1);
+ if (held && single_floating_window_node && IsMouseDragging(0, 0.0f))
+ {
+ // Move
+ StartMouseMovingWindow(docked_window);
+ }
+ else if (held && !tab_appearing && IsMouseDragging(0))
{
- if (!g.DragDropActive && (tab_bar->Flags & ImGuiTabBarFlags_Reorderable))
+ // 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);
}
}
+
+ // Extract a Dockable window out of it's tab bar
+ 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) //&& (!g.IO.ConfigDockingWithShift || g.IO.KeyShift)
+ {
+ float threshold_base = 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]
+
+ 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;
+ 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;
+ }
+
+ 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();
+ }
+ }
}
#if 0
@@ -8083,7 +8245,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);
@@ -8093,6 +8255,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();
@@ -8123,10 +8290,20 @@ 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()
}
+ else if (ImGuiWindow* window = FindWindowByName(label))
+ {
+ if (window->DockIsActive)
+ if (ImGuiDockNode* node = window->DockNode)
+ {
+ ImGuiID tab_id = TabBarCalcTabID(node->TabBar, label, window);
+ TabBarRemoveTab(node->TabBar, tab_id);
+ window->DockTabWantClose = true;
+ }
+ }
}
ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button)
@@ -8150,7 +8327,7 @@ void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabI
IM_ASSERT(width > 0.0f);
const float rounding = ImMax(0.0f, ImMin((flags & ImGuiTabItemFlags_Button) ? g.Style.FrameRounding : g.Style.TabRounding, width * 0.5f - 1.0f));
const float y1 = bb.Min.y + 1.0f;
- const float y2 = bb.Max.y - 1.0f;
+ const 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);
diff --git a/3rdparty/imgui/source/imstb_rectpack.h b/3rdparty/imgui/source/imstb_rectpack.h
index 3958952..f6917e7 100644
--- a/3rdparty/imgui/source/imstb_rectpack.h
+++ b/3rdparty/imgui/source/imstb_rectpack.h
@@ -1,15 +1,19 @@
// [DEAR IMGUI]
-// This is a slightly modified version of stb_rect_pack.h 1.00.
-// Those changes would need to be pushed into nothings/stb:
-// - Added STBRP__CDECL
+// This is a slightly modified version of stb_rect_pack.h 1.01.
// Grep for [DEAR IMGUI] to find the changes.
-
-// stb_rect_pack.h - v1.00 - public domain - rectangle packing
+//
+// stb_rect_pack.h - v1.01 - public domain - rectangle packing
// Sean Barrett 2014
//
// Useful for e.g. packing rectangular textures into an atlas.
// Does not do rotation.
//
+// Before #including,
+//
+// #define STB_RECT_PACK_IMPLEMENTATION
+//
+// in the file that you want to have the implementation.
+//
// Not necessarily the awesomest packing method, but better than
// the totally naive one in stb_truetype (which is primarily what
// this is meant to replace).
@@ -41,6 +45,7 @@
//
// Version history:
//
+// 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section
// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
// 0.99 (2019-02-07) warning fixes
// 0.11 (2017-03-03) return packing success/fail result
@@ -81,11 +86,10 @@ typedef struct stbrp_context stbrp_context;
typedef struct stbrp_node stbrp_node;
typedef struct stbrp_rect stbrp_rect;
-#ifdef STBRP_LARGE_RECTS
typedef int stbrp_coord;
-#else
-typedef unsigned short stbrp_coord;
-#endif
+
+#define STBRP__MAXVAL 0x7fffffff
+// Mostly for internal use, but this is the maximum supported coordinate value.
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
// Assign packed locations to rectangles. The rectangles are of type
@@ -213,10 +217,9 @@ struct stbrp_context
#define STBRP_ASSERT assert
#endif
-// [DEAR IMGUI] Added STBRP__CDECL
#ifdef _MSC_VER
#define STBRP__NOTUSED(v) (void)(v)
-#define STBRP__CDECL __cdecl
+#define STBRP__CDECL __cdecl
#else
#define STBRP__NOTUSED(v) (void)sizeof(v)
#define STBRP__CDECL
@@ -262,9 +265,6 @@ STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_ou
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
{
int i;
-#ifndef STBRP_LARGE_RECTS
- STBRP_ASSERT(width <= 0xffff && height <= 0xffff);
-#endif
for (i=0; i < num_nodes-1; ++i)
nodes[i].next = &nodes[i+1];
@@ -283,11 +283,7 @@ STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height,
context->extra[0].y = 0;
context->extra[0].next = &context->extra[1];
context->extra[1].x = (stbrp_coord) width;
-#ifdef STBRP_LARGE_RECTS
context->extra[1].y = (1<<30);
-#else
- context->extra[1].y = 65535;
-#endif
context->extra[1].next = NULL;
}
@@ -433,7 +429,7 @@ static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int widt
if (y <= best_y) {
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
best_x = xpos;
- STBRP_ASSERT(y <= best_y);
+ //STBRP_ASSERT(y <= best_y); [DEAR IMGUI]
best_y = y;
best_waste = waste;
best = prev;
@@ -529,7 +525,6 @@ static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, i
return res;
}
-// [DEAR IMGUI] Added STBRP__CDECL
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
{
const stbrp_rect *p = (const stbrp_rect *) a;
@@ -541,7 +536,6 @@ static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
return (p->w > q->w) ? -1 : (p->w < q->w);
}
-// [DEAR IMGUI] Added STBRP__CDECL
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
{
const stbrp_rect *p = (const stbrp_rect *) a;
@@ -549,12 +543,6 @@ static int STBRP__CDECL rect_original_order(const void *a, const void *b)
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
}
-#ifdef STBRP_LARGE_RECTS
-#define STBRP__MAXVAL 0xffffffff
-#else
-#define STBRP__MAXVAL 0xffff
-#endif
-
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
{
int i, all_rects_packed = 1;
diff --git a/3rdparty/imgui/source/imstb_textedit.h b/3rdparty/imgui/source/imstb_textedit.h
index 2c635b2..75a159d 100644
--- a/3rdparty/imgui/source/imstb_textedit.h
+++ b/3rdparty/imgui/source/imstb_textedit.h
@@ -1,10 +1,10 @@
// [DEAR IMGUI]
-// This is a slightly modified version of stb_textedit.h 1.13.
+// This is a slightly modified version of stb_textedit.h 1.14.
// Those changes would need to be pushed into nothings/stb:
// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)
// Grep for [DEAR IMGUI] to find the changes.
-// stb_textedit.h - v1.13 - public domain - Sean Barrett
+// stb_textedit.h - v1.14 - public domain - Sean Barrett
// Development of this library was sponsored by RAD Game Tools
//
// This C header file implements the guts of a multi-line text-editing
@@ -35,6 +35,7 @@
//
// VERSION HISTORY
//
+// 1.14 (2021-07-11) page up/down, various fixes
// 1.13 (2019-02-07) fix bug in undo size management
// 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash
// 1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield
@@ -58,6 +59,7 @@
// Ulf Winklemann: move-by-word in 1.1
// Fabian Giesen: secondary key inputs in 1.5
// Martins Mozeiko: STB_TEXTEDIT_memmove in 1.6
+// Louis Schnellbach: page up/down in 1.14
//
// Bugfixes:
// Scott Graham
@@ -93,8 +95,8 @@
// moderate sizes. The undo system does no memory allocations, so
// it grows STB_TexteditState by the worst-case storage which is (in bytes):
//
-// [4 + 3 * sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATE_COUNT
-// + sizeof(STB_TEXTEDIT_CHARTYPE) * STB_TEXTEDIT_UNDOCHAR_COUNT
+// [4 + 3 * sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATECOUNT
+// + sizeof(STB_TEXTEDIT_CHARTYPE) * STB_TEXTEDIT_UNDOCHARCOUNT
//
//
// Implementation mode:
@@ -716,10 +718,6 @@ static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditSta
state->has_preferred_x = 0;
return 1;
}
- // [DEAR IMGUI]
- //// remove the undo since we didn't actually insert the characters
- //if (state->undostate.undo_point)
- // --state->undostate.undo_point;
// note: paste failure will leave deleted selection, may be restored with an undo (see https://github.com/nothings/stb/issues/734 for details)
return 0;
}
diff --git a/3rdparty/imgui/source/imstb_truetype.h b/3rdparty/imgui/source/imstb_truetype.h
index 48c2026..643d378 100644
--- a/3rdparty/imgui/source/imstb_truetype.h
+++ b/3rdparty/imgui/source/imstb_truetype.h
@@ -1,10 +1,19 @@
// [DEAR IMGUI]
-// This is a slightly modified version of stb_truetype.h 1.20.
+// This is a slightly modified version of stb_truetype.h 1.26.
// Mostly fixing for compiler and static analyzer warnings.
// Grep for [DEAR IMGUI] to find the changes.
-// stb_truetype.h - v1.20 - public domain
-// authored from 2009-2016 by Sean Barrett / RAD Game Tools
+// stb_truetype.h - v1.26 - public domain
+// authored from 2009-2021 by Sean Barrett / RAD Game Tools
+//
+// =======================================================================
+//
+// NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES
+//
+// This library does no range checking of the offsets found in the file,
+// meaning an attacker can use it to read arbitrary memory.
+//
+// =======================================================================
//
// This library processes TrueType files:
// parse files
@@ -37,11 +46,11 @@
// Daniel Ribeiro Maciel
//
// Bug/warning reports/fixes:
-// "Zer" on mollyrocket Fabian "ryg" Giesen
-// Cass Everitt Martins Mozeiko
-// stoiko (Haemimont Games) Cap Petschulat
-// Brian Hook Omar Cornut
-// Walter van Niftrik github:aloucks
+// "Zer" on mollyrocket Fabian "ryg" Giesen github:NiLuJe
+// Cass Everitt Martins Mozeiko github:aloucks
+// stoiko (Haemimont Games) Cap Petschulat github:oyvindjam
+// Brian Hook Omar Cornut github:vassvik
+// Walter van Niftrik Ryan Griege
// David Gow Peter LaValle
// David Given Sergey Popov
// Ivan-Assen Ivanov Giumo X. Clanjor
@@ -49,11 +58,17 @@
// Johan Duparc Thomas Fields
// Hou Qiming Derek Vinyard
// Rob Loach Cort Stratton
-// Kenney Phillis Jr. github:oyvindjam
-// Brian Costabile github:vassvik
+// Kenney Phillis Jr. Brian Costabile
+// Ken Voskuil (kaesve)
//
// VERSION HISTORY
//
+// 1.26 (2021-08-28) fix broken rasterizer
+// 1.25 (2021-07-11) many fixes
+// 1.24 (2020-02-05) fix warning
+// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS)
+// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined
+// 1.21 (2019-02-25) fix warning
// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics()
// 1.19 (2018-02-11) GPOS kerning, STBTT_fmod
// 1.18 (2018-01-29) add missing function
@@ -248,19 +263,6 @@
// recommend it.
//
//
-// SOURCE STATISTICS (based on v0.6c, 2050 LOC)
-//
-// Documentation & header file 520 LOC \___ 660 LOC documentation
-// Sample code 140 LOC /
-// Truetype parsing 620 LOC ---- 620 LOC TrueType
-// Software rasterization 240 LOC \.
-// Curve tessellation 120 LOC \__ 550 LOC Bitmap creation
-// Bitmap management 100 LOC /
-// Baked bitmap interface 70 LOC /
-// Font name matching & access 150 LOC ---- 150
-// C runtime library abstraction 60 LOC ---- 60
-//
-//
// PERFORMANCE MEASUREMENTS FOR 1.06:
//
// 32-bit 64-bit
@@ -275,8 +277,8 @@
//// SAMPLE PROGRAMS
////
//
-// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless
-//
+// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless.
+// See "tests/truetype_demo_win32.c" for a complete version.
#if 0
#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
#include "stb_truetype.h"
@@ -302,6 +304,8 @@ void my_stbtt_initfont(void)
void my_stbtt_print(float x, float y, char *text)
{
// assume orthographic projection with units = screen pixels, origin at top left
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, ftex);
glBegin(GL_QUADS);
@@ -309,10 +313,10 @@ void my_stbtt_print(float x, float y, char *text)
if (*text >= 32 && *text < 128) {
stbtt_aligned_quad q;
stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9
- glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0);
- glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0);
- glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1);
- glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1);
+ glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y0);
+ glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0);
+ glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1);
+ glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1);
}
++text;
}
@@ -719,7 +723,7 @@ struct stbtt_fontinfo
int numGlyphs; // number of glyphs, needed for range checking
- int loca,head,glyf,hhea,hmtx,kern,gpos; // table locations as offset from start of .ttf
+ int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf
int index_map; // a cmap mapping for our chosen character encoding
int indexToLocFormat; // format needed to map from glyph index to glyph
@@ -802,6 +806,18 @@ STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1,
STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);
// as above, but takes one or more glyph indices for greater efficiency
+typedef struct stbtt_kerningentry
+{
+ int glyph1; // use stbtt_FindGlyphIndex
+ int glyph2;
+ int advance;
+} stbtt_kerningentry;
+
+STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info);
+STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length);
+// Retrieves a complete list of all of the kerning pairs provided by the font
+// stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write.
+// The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1)
//////////////////////////////////////////////////////////////////////////////
//
@@ -846,6 +862,12 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s
STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices);
// frees the data allocated above
+STBTT_DEF unsigned char *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl);
+STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg);
+STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg);
+// fills svg with the character's SVG data.
+// returns data size or 0 if SVG not found.
+
//////////////////////////////////////////////////////////////////////////////
//
// BITMAP RENDERING
@@ -1347,6 +1369,22 @@ static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict)
return stbtt__cff_get_index(&cff);
}
+// since most people won't use this, find this table the first time it's needed
+static int stbtt__get_svg(stbtt_fontinfo *info)
+{
+ stbtt_uint32 t;
+ if (info->svg < 0) {
+ t = stbtt__find_table(info->data, info->fontstart, "SVG ");
+ if (t) {
+ stbtt_uint32 offset = ttULONG(info->data + t + 2);
+ info->svg = t + offset;
+ } else {
+ info->svg = 0;
+ }
+ }
+ return info->svg;
+}
+
static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart)
{
stbtt_uint32 cmap, t;
@@ -1426,6 +1464,8 @@ static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, in
else
info->numGlyphs = 0xffff;
+ info->svg = -1;
+
// find a cmap encoding table we understand *now* to avoid searching
// later. (todo: could make this installable)
// the same regardless of glyph.
@@ -1509,12 +1549,12 @@ STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codep
search += 2;
{
- stbtt_uint16 offset, start;
+ stbtt_uint16 offset, start, last;
stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1);
- STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item));
start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item);
- if (unicode_codepoint < start)
+ last = ttUSHORT(data + endCount + 2*item);
+ if (unicode_codepoint < start || unicode_codepoint > last)
return 0;
offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item);
@@ -1774,7 +1814,7 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, s
}
}
num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);
- } else if (numberOfContours == -1) {
+ } else if (numberOfContours < 0) {
// Compound shapes.
int more = 1;
stbtt_uint8 *comp = data + g + 10;
@@ -1841,7 +1881,7 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, s
if (comp_verts) STBTT_free(comp_verts, info->userdata);
return 0;
}
- if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); //-V595
+ if (num_vertices > 0 && vertices) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex));
STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex));
if (vertices) STBTT_free(vertices, info->userdata);
vertices = tmp;
@@ -1851,9 +1891,6 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, s
// More components ?
more = flags & (1<<5);
}
- } else if (numberOfContours < 0) {
- // @TODO other compound variations?
- STBTT_assert(0);
} else {
// numberOfCounters == 0, do nothing
}
@@ -2107,7 +2144,7 @@ static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, st
subrs = stbtt__cid_get_glyph_subrs(info, glyph_index);
has_subrs = 1;
}
- // fallthrough
+ // FALLTHROUGH
case 0x1D: // callgsubr
if (sp < 1) return STBTT__CSERR("call(g|)subr stack");
v = (int) s[--sp];
@@ -2212,7 +2249,7 @@ static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, st
} break;
default:
- if (b0 != 255 && b0 != 28 && (b0 < 32 || b0 > 254)) //-V560
+ if (b0 != 255 && b0 != 28 && b0 < 32)
return STBTT__CSERR("reserved operator");
// push immediate
@@ -2282,7 +2319,49 @@ STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_inde
}
}
-static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
+STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info)
+{
+ stbtt_uint8 *data = info->data + info->kern;
+
+ // we only look at the first table. it must be 'horizontal' and format 0.
+ if (!info->kern)
+ return 0;
+ if (ttUSHORT(data+2) < 1) // number of tables, need at least 1
+ return 0;
+ if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format
+ return 0;
+
+ return ttUSHORT(data+10);
+}
+
+STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length)
+{
+ stbtt_uint8 *data = info->data + info->kern;
+ int k, length;
+
+ // we only look at the first table. it must be 'horizontal' and format 0.
+ if (!info->kern)
+ return 0;
+ if (ttUSHORT(data+2) < 1) // number of tables, need at least 1
+ return 0;
+ if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format
+ return 0;
+
+ length = ttUSHORT(data+10);
+ if (table_length < length)
+ length = table_length;
+
+ for (k = 0; k < length; k++)
+ {
+ table[k].glyph1 = ttUSHORT(data+18+(k*6));
+ table[k].glyph2 = ttUSHORT(data+20+(k*6));
+ table[k].advance = ttSHORT(data+22+(k*6));
+ }
+
+ return length;
+}
+
+static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
{
stbtt_uint8 *data = info->data + info->kern;
stbtt_uint32 needle, straw;
@@ -2312,245 +2391,225 @@ static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph
return 0;
}
-static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph)
+static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph)
{
- stbtt_uint16 coverageFormat = ttUSHORT(coverageTable);
- switch(coverageFormat) {
- case 1: {
- stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2);
+ stbtt_uint16 coverageFormat = ttUSHORT(coverageTable);
+ switch (coverageFormat) {
+ case 1: {
+ stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2);
- // Binary search.
- stbtt_int32 l=0, r=glyphCount-1, m;
- int straw, needle=glyph;
- while (l <= r) {
- stbtt_uint8 *glyphArray = coverageTable + 4;
- stbtt_uint16 glyphID;
- m = (l + r) >> 1;
- glyphID = ttUSHORT(glyphArray + 2 * m);
- straw = glyphID;
- if (needle < straw)
- r = m - 1;
- else if (needle > straw)
- l = m + 1;
- else {
- return m;
- }
+ // Binary search.
+ stbtt_int32 l=0, r=glyphCount-1, m;
+ int straw, needle=glyph;
+ while (l <= r) {
+ stbtt_uint8 *glyphArray = coverageTable + 4;
+ stbtt_uint16 glyphID;
+ m = (l + r) >> 1;
+ glyphID = ttUSHORT(glyphArray + 2 * m);
+ straw = glyphID;
+ if (needle < straw)
+ r = m - 1;
+ else if (needle > straw)
+ l = m + 1;
+ else {
+ return m;
}
- } break;
+ }
+ break;
+ }
- case 2: {
- stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2);
- stbtt_uint8 *rangeArray = coverageTable + 4;
+ case 2: {
+ stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2);
+ stbtt_uint8 *rangeArray = coverageTable + 4;
- // Binary search.
- stbtt_int32 l=0, r=rangeCount-1, m;
- int strawStart, strawEnd, needle=glyph;
- while (l <= r) {
- stbtt_uint8 *rangeRecord;
- m = (l + r) >> 1;
- rangeRecord = rangeArray + 6 * m;
- strawStart = ttUSHORT(rangeRecord);
- strawEnd = ttUSHORT(rangeRecord + 2);
- if (needle < strawStart)
- r = m - 1;
- else if (needle > strawEnd)
- l = m + 1;
- else {
- stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4);
- return startCoverageIndex + glyph - strawStart;
- }
+ // Binary search.
+ stbtt_int32 l=0, r=rangeCount-1, m;
+ int strawStart, strawEnd, needle=glyph;
+ while (l <= r) {
+ stbtt_uint8 *rangeRecord;
+ m = (l + r) >> 1;
+ rangeRecord = rangeArray + 6 * m;
+ strawStart = ttUSHORT(rangeRecord);
+ strawEnd = ttUSHORT(rangeRecord + 2);
+ if (needle < strawStart)
+ r = m - 1;
+ else if (needle > strawEnd)
+ l = m + 1;
+ else {
+ stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4);
+ return startCoverageIndex + glyph - strawStart;
}
- } break;
+ }
+ break;
+ }
- default: {
- // There are no other cases.
- STBTT_assert(0);
- } break;
- }
+ default: return -1; // unsupported
+ }
- return -1;
+ return -1;
}
static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph)
{
- stbtt_uint16 classDefFormat = ttUSHORT(classDefTable);
- switch(classDefFormat)
- {
- case 1: {
- stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2);
- stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4);
- stbtt_uint8 *classDef1ValueArray = classDefTable + 6;
-
- if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount)
- return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID));
-
- // [DEAR IMGUI] Commented to fix static analyzer warning
- //classDefTable = classDef1ValueArray + 2 * glyphCount;
- } break;
+ stbtt_uint16 classDefFormat = ttUSHORT(classDefTable);
+ switch (classDefFormat)
+ {
+ case 1: {
+ stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2);
+ stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4);
+ stbtt_uint8 *classDef1ValueArray = classDefTable + 6;
- case 2: {
- stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2);
- stbtt_uint8 *classRangeRecords = classDefTable + 4;
+ if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount)
+ return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID));
+ break;
+ }
- // Binary search.
- stbtt_int32 l=0, r=classRangeCount-1, m;
- int strawStart, strawEnd, needle=glyph;
- while (l <= r) {
- stbtt_uint8 *classRangeRecord;
- m = (l + r) >> 1;
- classRangeRecord = classRangeRecords + 6 * m;
- strawStart = ttUSHORT(classRangeRecord);
- strawEnd = ttUSHORT(classRangeRecord + 2);
- if (needle < strawStart)
- r = m - 1;
- else if (needle > strawEnd)
- l = m + 1;
- else
- return (stbtt_int32)ttUSHORT(classRangeRecord + 4);
- }
+ case 2: {
+ stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2);
+ stbtt_uint8 *classRangeRecords = classDefTable + 4;
- // [DEAR IMGUI] Commented to fix static analyzer warning
- //classDefTable = classRangeRecords + 6 * classRangeCount;
- } break;
+ // Binary search.
+ stbtt_int32 l=0, r=classRangeCount-1, m;
+ int strawStart, strawEnd, needle=glyph;
+ while (l <= r) {
+ stbtt_uint8 *classRangeRecord;
+ m = (l + r) >> 1;
+ classRangeRecord = classRangeRecords + 6 * m;
+ strawStart = ttUSHORT(classRangeRecord);
+ strawEnd = ttUSHORT(classRangeRecord + 2);
+ if (needle < strawStart)
+ r = m - 1;
+ else if (needle > strawEnd)
+ l = m + 1;
+ else
+ return (stbtt_int32)ttUSHORT(classRangeRecord + 4);
+ }
+ break;
+ }
- default: {
- // There are no other cases.
- STBTT_assert(0);
- } break;
- }
+ default:
+ return -1; // Unsupported definition type, return an error.
+ }
- return -1;
+ // "All glyphs not assigned to a class fall into class 0". (OpenType spec)
+ return 0;
}
// Define to STBTT_assert(x) if you want to break on unimplemented formats.
#define STBTT_GPOS_TODO_assert(x)
-static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
+static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
{
- stbtt_uint16 lookupListOffset;
- stbtt_uint8 *lookupList;
- stbtt_uint16 lookupCount;
- stbtt_uint8 *data;
- stbtt_int32 i;
-
- if (!info->gpos) return 0;
+ stbtt_uint16 lookupListOffset;
+ stbtt_uint8 *lookupList;
+ stbtt_uint16 lookupCount;
+ stbtt_uint8 *data;
+ stbtt_int32 i, sti;
- data = info->data + info->gpos;
+ if (!info->gpos) return 0;
- if (ttUSHORT(data+0) != 1) return 0; // Major version 1
- if (ttUSHORT(data+2) != 0) return 0; // Minor version 0
+ data = info->data + info->gpos;
- lookupListOffset = ttUSHORT(data+8);
- lookupList = data + lookupListOffset;
- lookupCount = ttUSHORT(lookupList);
+ if (ttUSHORT(data+0) != 1) return 0; // Major version 1
+ if (ttUSHORT(data+2) != 0) return 0; // Minor version 0
- for (i=0; i<lookupCount; ++i) {
- stbtt_uint16 lookupOffset = ttUSHORT(lookupList + 2 + 2 * i);
- stbtt_uint8 *lookupTable = lookupList + lookupOffset;
+ lookupListOffset = ttUSHORT(data+8);
+ lookupList = data + lookupListOffset;
+ lookupCount = ttUSHORT(lookupList);
- stbtt_uint16 lookupType = ttUSHORT(lookupTable);
- stbtt_uint16 subTableCount = ttUSHORT(lookupTable + 4);
- stbtt_uint8 *subTableOffsets = lookupTable + 6;
- switch(lookupType) {
- case 2: { // Pair Adjustment Positioning Subtable
- stbtt_int32 sti;
- for (sti=0; sti<subTableCount; sti++) {
- stbtt_uint16 subtableOffset = ttUSHORT(subTableOffsets + 2 * sti);
- stbtt_uint8 *table = lookupTable + subtableOffset;
- stbtt_uint16 posFormat = ttUSHORT(table);
- stbtt_uint16 coverageOffset = ttUSHORT(table + 2);
- stbtt_int32 coverageIndex = stbtt__GetCoverageIndex(table + coverageOffset, glyph1);
- if (coverageIndex == -1) continue;
+ for (i=0; i<lookupCount; ++i) {
+ stbtt_uint16 lookupOffset = ttUSHORT(lookupList + 2 + 2 * i);
+ stbtt_uint8 *lookupTable = lookupList + lookupOffset;
- switch (posFormat) {
- case 1: {
- stbtt_int32 l, r, m;
- int straw, needle;
- stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
- stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
- stbtt_int32 valueRecordPairSizeInBytes = 2;
- stbtt_uint16 pairSetCount = ttUSHORT(table + 8);
- stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex);
- stbtt_uint8 *pairValueTable = table + pairPosOffset;
- stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable);
- stbtt_uint8 *pairValueArray = pairValueTable + 2;
- // TODO: Support more formats.
- STBTT_GPOS_TODO_assert(valueFormat1 == 4);
- if (valueFormat1 != 4) return 0;
- STBTT_GPOS_TODO_assert(valueFormat2 == 0);
- if (valueFormat2 != 0) return 0;
+ stbtt_uint16 lookupType = ttUSHORT(lookupTable);
+ stbtt_uint16 subTableCount = ttUSHORT(lookupTable + 4);
+ stbtt_uint8 *subTableOffsets = lookupTable + 6;
+ if (lookupType != 2) // Pair Adjustment Positioning Subtable
+ continue;
- STBTT_assert(coverageIndex < pairSetCount);
- STBTT__NOTUSED(pairSetCount);
+ for (sti=0; sti<subTableCount; sti++) {
+ stbtt_uint16 subtableOffset = ttUSHORT(subTableOffsets + 2 * sti);
+ stbtt_uint8 *table = lookupTable + subtableOffset;
+ stbtt_uint16 posFormat = ttUSHORT(table);
+ stbtt_uint16 coverageOffset = ttUSHORT(table + 2);
+ stbtt_int32 coverageIndex = stbtt__GetCoverageIndex(table + coverageOffset, glyph1);
+ if (coverageIndex == -1) continue;
- needle=glyph2;
- r=pairValueCount-1;
- l=0;
+ switch (posFormat) {
+ case 1: {
+ stbtt_int32 l, r, m;
+ int straw, needle;
+ stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
+ stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
+ if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats?
+ stbtt_int32 valueRecordPairSizeInBytes = 2;
+ stbtt_uint16 pairSetCount = ttUSHORT(table + 8);
+ stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex);
+ stbtt_uint8 *pairValueTable = table + pairPosOffset;
+ stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable);
+ stbtt_uint8 *pairValueArray = pairValueTable + 2;
- // Binary search.
- while (l <= r) {
- stbtt_uint16 secondGlyph;
- stbtt_uint8 *pairValue;
- m = (l + r) >> 1;
- pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m;
- secondGlyph = ttUSHORT(pairValue);
- straw = secondGlyph;
- if (needle < straw)
- r = m - 1;
- else if (needle > straw)
- l = m + 1;
- else {
- stbtt_int16 xAdvance = ttSHORT(pairValue + 2);
- return xAdvance;
- }
- }
- } break;
+ if (coverageIndex >= pairSetCount) return 0;
- case 2: {
- stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
- stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
+ needle=glyph2;
+ r=pairValueCount-1;
+ l=0;
- stbtt_uint16 classDef1Offset = ttUSHORT(table + 8);
- stbtt_uint16 classDef2Offset = ttUSHORT(table + 10);
- int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1);
- int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2);
+ // Binary search.
+ while (l <= r) {
+ stbtt_uint16 secondGlyph;
+ stbtt_uint8 *pairValue;
+ m = (l + r) >> 1;
+ pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m;
+ secondGlyph = ttUSHORT(pairValue);
+ straw = secondGlyph;
+ if (needle < straw)
+ r = m - 1;
+ else if (needle > straw)
+ l = m + 1;
+ else {
+ stbtt_int16 xAdvance = ttSHORT(pairValue + 2);
+ return xAdvance;
+ }
+ }
+ } else
+ return 0;
+ break;
+ }
- stbtt_uint16 class1Count = ttUSHORT(table + 12);
- stbtt_uint16 class2Count = ttUSHORT(table + 14);
- STBTT_assert(glyph1class < class1Count);
- STBTT_assert(glyph2class < class2Count);
+ case 2: {
+ stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
+ stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
+ if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats?
+ stbtt_uint16 classDef1Offset = ttUSHORT(table + 8);
+ stbtt_uint16 classDef2Offset = ttUSHORT(table + 10);
+ int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1);
+ int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2);
- // TODO: Support more formats.
- STBTT_GPOS_TODO_assert(valueFormat1 == 4);
- if (valueFormat1 != 4) return 0;
- STBTT_GPOS_TODO_assert(valueFormat2 == 0);
- if (valueFormat2 != 0) return 0;
+ stbtt_uint16 class1Count = ttUSHORT(table + 12);
+ stbtt_uint16 class2Count = ttUSHORT(table + 14);
+ stbtt_uint8 *class1Records, *class2Records;
+ stbtt_int16 xAdvance;
- if (glyph1class >= 0 && glyph1class < class1Count && glyph2class >= 0 && glyph2class < class2Count) {
- stbtt_uint8 *class1Records = table + 16;
- stbtt_uint8 *class2Records = class1Records + 2 * (glyph1class * class2Count);
- stbtt_int16 xAdvance = ttSHORT(class2Records + 2 * glyph2class);
- return xAdvance;
- }
- } break;
+ if (glyph1class < 0 || glyph1class >= class1Count) return 0; // malformed
+ if (glyph2class < 0 || glyph2class >= class2Count) return 0; // malformed
- default: {
- // There are no other cases.
- STBTT_assert(0);
- break;
- } // [DEAR IMGUI] removed ;
- }
- }
- break;
- } // [DEAR IMGUI] removed ;
+ class1Records = table + 16;
+ class2Records = class1Records + 2 * (glyph1class * class2Count);
+ xAdvance = ttSHORT(class2Records + 2 * glyph2class);
+ return xAdvance;
+ } else
+ return 0;
+ break;
+ }
default:
- // TODO: Implement other stuff.
- break;
- }
- }
+ return 0; // Unsupported position format
+ }
+ }
+ }
- return 0;
+ return 0;
}
STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2)
@@ -2559,8 +2618,7 @@ STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int
if (info->gpos)
xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2);
-
- if (info->kern)
+ else if (info->kern)
xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2);
return xAdvance;
@@ -2621,6 +2679,45 @@ STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)
STBTT_free(v, info->userdata);
}
+STBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl)
+{
+ int i;
+ stbtt_uint8 *data = info->data;
+ stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info);
+
+ int numEntries = ttUSHORT(svg_doc_list);
+ stbtt_uint8 *svg_docs = svg_doc_list + 2;
+
+ for(i=0; i<numEntries; i++) {
+ stbtt_uint8 *svg_doc = svg_docs + (12 * i);
+ if ((gl >= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2)))
+ return svg_doc;
+ }
+ return 0;
+}
+
+STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg)
+{
+ stbtt_uint8 *data = info->data;
+ stbtt_uint8 *svg_doc;
+
+ if (info->svg == 0)
+ return 0;
+
+ svg_doc = stbtt_FindSVGDoc(info, gl);
+ if (svg_doc != NULL) {
+ *svg = (char *) data + info->svg + ttULONG(svg_doc + 4);
+ return ttULONG(svg_doc + 8);
+ } else {
+ return 0;
+ }
+}
+
+STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg)
+{
+ return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg);
+}
+
//////////////////////////////////////////////////////////////////////////////
//
// antialiasing software rasterizer
@@ -2970,6 +3067,23 @@ static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edg
}
}
+static float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width)
+{
+ STBTT_assert(top_width >= 0);
+ STBTT_assert(bottom_width >= 0);
+ return (top_width + bottom_width) / 2.0f * height;
+}
+
+static float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1)
+{
+ return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0);
+}
+
+static float stbtt__sized_triangle_area(float height, float width)
+{
+ return height * width / 2;
+}
+
static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top)
{
float y_bottom = y_top+1;
@@ -3024,13 +3138,13 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
float height;
// simple case, only spans one pixel
int x = (int) x_top;
- height = sy1 - sy0;
+ height = (sy1 - sy0) * e->direction;
STBTT_assert(x >= 0 && x < len);
- scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height;
- scanline_fill[x] += e->direction * height; // everything right of this pixel is filled
+ scanline[x] += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f);
+ scanline_fill[x] += height; // everything right of this pixel is filled
} else {
int x,x1,x2;
- float y_crossing, step, sign, area;
+ float y_crossing, y_final, step, sign, area;
// covers 2+ pixels
if (x_top > x_bottom) {
// flip scanline vertically; signed area is the same
@@ -3042,32 +3156,83 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
dx = -dx;
dy = -dy;
t = x0, x0 = xb, xb = t;
- // [DEAR IMGUI] Fix static analyzer warning
- (void)dx; // [ImGui: fix static analyzer warning]
}
+ STBTT_assert(dy >= 0);
+ STBTT_assert(dx >= 0);
x1 = (int) x_top;
x2 = (int) x_bottom;
// compute intersection with y axis at x1+1
- y_crossing = (x1+1 - x0) * dy + y_top;
+ y_crossing = y_top + dy * (x1+1 - x0);
+
+ // compute intersection with y axis at x2
+ y_final = y_top + dy * (x2 - x0);
+
+ // x1 x_top x2 x_bottom
+ // y_top +------|-----+------------+------------+--------|---+------------+
+ // | | | | | |
+ // | | | | | |
+ // sy0 | Txxxxx|............|............|............|............|
+ // y_crossing | *xxxxx.......|............|............|............|
+ // | | xxxxx..|............|............|............|
+ // | | /- xx*xxxx........|............|............|
+ // | | dy < | xxxxxx..|............|............|
+ // y_final | | \- | xx*xxx.........|............|
+ // sy1 | | | | xxxxxB...|............|
+ // | | | | | |
+ // | | | | | |
+ // y_bottom +------------+------------+------------+------------+------------+
+ //
+ // goal is to measure the area covered by '.' in each pixel
+
+ // if x2 is right at the right edge of x1, y_crossing can blow up, github #1057
+ // @TODO: maybe test against sy1 rather than y_bottom?
+ if (y_crossing > y_bottom)
+ y_crossing = y_bottom;
sign = e->direction;
- // area of the rectangle covered from y0..y_crossing
+
+ // area of the rectangle covered from sy0..y_crossing
area = sign * (y_crossing-sy0);
- // area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing)
- scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2);
- step = sign * dy;
+ // area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing)
+ scanline[x1] += stbtt__sized_triangle_area(area, x1+1 - x_top);
+
+ // check if final y_crossing is blown up; no test case for this
+ if (y_final > y_bottom) {
+ int denom = (x2 - (x1+1));
+ y_final = y_bottom;
+ if (denom != 0) { // [DEAR IMGUI] Avoid div by zero (https://github.com/nothings/stb/issues/1316)
+ dy = (y_final - y_crossing ) / denom; // if denom=0, y_final = y_crossing, so y_final <= y_bottom
+ }
+ }
+
+ // in second pixel, area covered by line segment found in first pixel
+ // is always a rectangle 1 wide * the height of that line segment; this
+ // is exactly what the variable 'area' stores. it also gets a contribution
+ // from the line segment within it. the THIRD pixel will get the first
+ // pixel's rectangle contribution, the second pixel's rectangle contribution,
+ // and its own contribution. the 'own contribution' is the same in every pixel except
+ // the leftmost and rightmost, a trapezoid that slides down in each pixel.
+ // the second pixel's contribution to the third pixel will be the
+ // rectangle 1 wide times the height change in the second pixel, which is dy.
+
+ step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x,
+ // which multiplied by 1-pixel-width is how much pixel area changes for each step in x
+ // so the area advances by 'step' every time
+
for (x = x1+1; x < x2; ++x) {
- scanline[x] += area + step/2;
+ scanline[x] += area + step/2; // area of trapezoid is 1*step/2
area += step;
}
- y_crossing += dy * (x2 - (x1+1));
-
- STBTT_assert(STBTT_fabs(area) <= 1.01f);
+ STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down
+ STBTT_assert(sy1 > y_final-0.01f);
- scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing);
+ // area covered in the last pixel is the rectangle from all the pixels to the left,
+ // plus the trapezoid filled by the line segment in this pixel all the way to the right edge
+ scanline[x2] += area + sign * stbtt__position_trapezoid_area(sy1-y_final, (float) x2, x2+1.0f, x_bottom, x2+1.0f);
+ // the rest of the line is filled based on the total height of the line segment in this pixel
scanline_fill[x2] += sign * (sy1-sy0);
}
} else {
@@ -3075,6 +3240,9 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
// clipping logic. since this does not match the intended use
// of this library, we use a different, very slow brute
// force implementation
+ // note though that this does happen some of the time because
+ // x_top and x_bottom can be extrapolated at the top & bottom of
+ // the shape and actually lie outside the bounding box
int x;
for (x=0; x < len; ++x) {
// cases:
@@ -3989,6 +4157,7 @@ static float stbtt__oversample_shift(int oversample)
STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
{
int i,j,k;
+ int missing_glyph_added = 0;
k=0;
for (i=0; i < num_ranges; ++i) {
@@ -4000,7 +4169,7 @@ STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stb
int x0,y0,x1,y1;
int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];
int glyph = stbtt_FindGlyphIndex(info, codepoint);
- if (glyph == 0 && spc->skip_missing) {
+ if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) {
rects[k].w = rects[k].h = 0;
} else {
stbtt_GetGlyphBitmapBoxSubpixel(info,glyph,
@@ -4010,6 +4179,8 @@ STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stb
&x0,&y0,&x1,&y1);
rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1);
rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1);
+ if (glyph == 0)
+ missing_glyph_added = 1;
}
++k;
}
@@ -4044,7 +4215,7 @@ STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info
// rects array must be big enough to accommodate all characters in the given ranges
STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
{
- int i,j,k, return_value = 1;
+ int i,j,k, missing_glyph = -1, return_value = 1;
// save current values
int old_h_over = spc->h_oversample;
@@ -4109,6 +4280,13 @@ STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const
bc->yoff = (float) y0 * recip_v + sub_y;
bc->xoff2 = (x0 + r->w) * recip_h + sub_x;
bc->yoff2 = (y0 + r->h) * recip_v + sub_y;
+
+ if (glyph == 0)
+ missing_glyph = j;
+ } else if (spc->skip_missing) {
+ return_value = 0;
+ } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) {
+ ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph];
} else {
return_value = 0; // if any fail, report failure
}
@@ -4132,7 +4310,7 @@ STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect
STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges)
{
stbtt_fontinfo info;
- int i,j,n, return_value; // [DEAR IMGUI] removed = 1
+ int i, j, n, return_value; // [DEAR IMGUI] removed = 1;
//stbrp_context *context = (stbrp_context *) spc->pack_info;
stbrp_rect *rects;
@@ -4301,15 +4479,14 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex
float y_frac;
int winding = 0;
- orig[0] = x;
- //orig[1] = y; // [DEAR IMGUI] commented double assignment
-
// make sure y never passes through a vertex of the shape
y_frac = (float) STBTT_fmod(y, 1.0f);
if (y_frac < 0.01f)
y += 0.01f;
else if (y_frac > 0.99f)
y -= 0.01f;
+
+ orig[0] = x;
orig[1] = y;
// test a ray from (-infinity,y) to (x,y)
@@ -4371,35 +4548,35 @@ static float stbtt__cuberoot( float x )
return (float) STBTT_pow( x,1.0f/3.0f);
}
-// x^3 + c*x^2 + b*x + a = 0
+// x^3 + a*x^2 + b*x + c = 0
static int stbtt__solve_cubic(float a, float b, float c, float* r)
{
- float s = -a / 3;
- float p = b - a*a / 3;
- float q = a * (2*a*a - 9*b) / 27 + c;
+ float s = -a / 3;
+ float p = b - a*a / 3;
+ float q = a * (2*a*a - 9*b) / 27 + c;
float p3 = p*p*p;
- float d = q*q + 4*p3 / 27;
- if (d >= 0) {
- float z = (float) STBTT_sqrt(d);
- float u = (-q + z) / 2;
- float v = (-q - z) / 2;
- u = stbtt__cuberoot(u);
- v = stbtt__cuberoot(v);
- r[0] = s + u + v;
- return 1;
- } else {
- float u = (float) STBTT_sqrt(-p/3);
- float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative
- float m = (float) STBTT_cos(v);
+ float d = q*q + 4*p3 / 27;
+ if (d >= 0) {
+ float z = (float) STBTT_sqrt(d);
+ float u = (-q + z) / 2;
+ float v = (-q - z) / 2;
+ u = stbtt__cuberoot(u);
+ v = stbtt__cuberoot(v);
+ r[0] = s + u + v;
+ return 1;
+ } else {
+ float u = (float) STBTT_sqrt(-p/3);
+ float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative
+ float m = (float) STBTT_cos(v);
float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f;
- r[0] = s + u * 2 * m;
- r[1] = s - u * (m + n);
- r[2] = s - u * (m - n);
+ r[0] = s + u * 2 * m;
+ r[1] = s - u * (m + n);
+ r[2] = s - u * (m - n);
//STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe?
//STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f);
//STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f);
- return 3;
+ return 3;
}
}
@@ -4410,12 +4587,7 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc
int w,h;
unsigned char *data;
- // if one scale is 0, use same scale for both
- if (scale_x == 0) scale_x = scale_y;
- if (scale_y == 0) {
- if (scale_x == 0) return NULL; // if both scales are 0, return NULL
- scale_y = scale_x;
- }
+ if (scale == 0) return NULL;
stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1);
@@ -4481,18 +4653,17 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc
for (i=0; i < num_verts; ++i) {
float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y;
- // check against every point here rather than inside line/curve primitives -- @TODO: wrong if multiple 'moves' in a row produce a garbage point, and given culling, probably more efficient to do within line/curve
- float dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy);
- if (dist2 < min_dist*min_dist)
- min_dist = (float) STBTT_sqrt(dist2);
-
- if (verts[i].type == STBTT_vline) {
+ if (verts[i].type == STBTT_vline && precompute[i] != 0.0f) {
float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y;
+ float dist,dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy);
+ if (dist2 < min_dist*min_dist)
+ min_dist = (float) STBTT_sqrt(dist2);
+
// coarse culling against bbox
//if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist &&
// sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist)
- float dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i];
+ dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i];
STBTT_assert(i != 0);
if (dist < min_dist) {
// check position along line
@@ -4519,7 +4690,8 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc
float ax = x1-x0, ay = y1-y0;
float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;
float mx = x0 - sx, my = y0 - sy;
- float res[3],px,py,t,it;
+ float res[3] = {0.f,0.f,0.f};
+ float px,py,t,it,dist2;
float a_inv = precompute[i];
if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula
float a = 3*(ax*bx + ay*by);
@@ -4546,6 +4718,10 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc
float d = (mx*ax+my*ay) * a_inv;
num = stbtt__solve_cubic(b, c, d, res);
}
+ dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy);
+ if (dist2 < min_dist*min_dist)
+ min_dist = (float) STBTT_sqrt(dist2);
+
if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) {
t = res[0], it = 1.0f - t;
px = it*it*x0 + 2*t*it*x1 + t*t*x2;
@@ -4805,6 +4981,12 @@ STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const
// FULL VERSION HISTORY
//
+// 1.25 (2021-07-11) many fixes
+// 1.24 (2020-02-05) fix warning
+// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS)
+// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined
+// 1.21 (2019-02-25) fix warning
+// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics()
// 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod
// 1.18 (2018-01-29) add missing function
// 1.17 (2017-07-23) make more arguments const; doc fix