diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index d5d3769ee..6c074b26b 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -25,6 +25,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2023-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2023-12-19: Emscripten: Added ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback() to register canvas selector and auto-resize GLFW window. // 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys. // 2023-07-18: Inputs: Revert ignoring mouse data on GLFW_CURSOR_DISABLED as it can be used differently. User may set ImGuiConfigFLags_NoMouse if desired. (#5625, #6609) // 2023-06-12: Accept glfwGetTime() not returning a monotonically increasing value. This seems to happens on some Windows setup when peripherals disconnect, and is likely to also happen on browser + Emscripten. (#6491) @@ -152,6 +153,9 @@ struct ImGui_ImplGlfw_Data bool InstalledCallbacks; bool CallbacksChainForAllWindows; bool WantUpdateMonitors; +#ifdef __EMSCRIPTEN__ + const char* CanvasSelector; +#endif // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. GLFWwindowfocusfun PrevUserCallbackWindowFocus; @@ -923,6 +927,43 @@ void ImGui_ImplGlfw_NewFrame() ImGui_ImplGlfw_UpdateGamepads(); } +#ifdef __EMSCRIPTEN__ +static EM_BOOL ImGui_ImplGlfw_OnCanvasSizeChange(int event_type, const EmscriptenUiEvent* event, void* user_data) +{ + ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)user_data; + double canvas_width, canvas_height; + emscripten_get_element_css_size(bd->CanvasSelector, &canvas_width, &canvas_height); + glfwSetWindowSize(bd->Window, (int)canvas_width, (int)canvas_height); + return true; +} + +static EM_BOOL ImGui_ImplEmscripten_FullscreenChangeCallback(int event_type, const EmscriptenFullscreenChangeEvent* event, void* user_data) +{ + ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)user_data; + double canvas_width, canvas_height; + emscripten_get_element_css_size(bd->CanvasSelector, &canvas_width, &canvas_height); + glfwSetWindowSize(bd->Window, (int)canvas_width, (int)canvas_height); + return true; +} + +// 'canvas_selector' is a CSS selector. The event listener is applied to the first element that matches the query. +// STRING MUST PERSIST FOR THE APPLICATION DURATION. PLEASE USE A STRING LITERAL OR ENSURE POINTER WILL STAY VALID. +void ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_selector) +{ + IM_ASSERT(canvas_selector != nullptr); + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplGlfw_InitForXXX()?"); + + bd->CanvasSelector = canvas_selector; + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, bd, false, ImGui_ImplGlfw_OnCanvasSizeChange); + emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, bd, false, ImGui_ImplEmscripten_FullscreenChangeCallback); + + // Change the size of the GLFW window according to the size of the canvas + ImGui_ImplGlfw_OnCanvasSizeChange(EMSCRIPTEN_EVENT_RESIZE, {}, bd); +} +#endif + + //-------------------------------------------------------------------------------------------------------- // MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT // This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously. @@ -1264,7 +1305,7 @@ static void ImGui_ImplGlfw_ShutdownPlatformInterface() //----------------------------------------------------------------------------- -// WndProc hook (declared here because we will need access to ImGui_ImplGlfw_ViewportData) +// WndProc hook (declared here because we will need access to ImGui_ImplGlfw_ViewportData) #ifdef _WIN32 static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo() { diff --git a/backends/imgui_impl_glfw.h b/backends/imgui_impl_glfw.h index d2fcbbb8b..14e6ee6a7 100644 --- a/backends/imgui_impl_glfw.h +++ b/backends/imgui_impl_glfw.h @@ -35,6 +35,11 @@ IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool ins IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown(); IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame(); +// Emscripten related initialization phase methods +#ifdef __EMSCRIPTEN__ +IMGUI_IMPL_API void ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_selector); +#endif + // GLFW callbacks install // - When calling Init with 'install_callbacks=true': ImGui_ImplGlfw_InstallCallbacks() is called. GLFW callbacks will be installed for you. They will chain-call user's previously installed callbacks, if any. // - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call individual function yourself from your own GLFW callbacks. diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index c02aada2d..a5659351d 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -812,7 +812,7 @@ bool ImGui_ImplVulkan_CreateFontsTexture() err = vkQueueSubmit(v->Queue, 1, &end_info, VK_NULL_HANDLE); check_vk_result(err); - err = vkDeviceWaitIdle(v->Device); + err = vkQueueWaitIdle(v->Queue); check_vk_result(err); vkDestroyBuffer(v->Device, upload_buffer, v->Allocator); @@ -1049,6 +1049,7 @@ void ImGui_ImplVulkan_DestroyDeviceObjects() ImGui_ImplVulkanH_DestroyAllViewportsRenderBuffers(v->Device, v->Allocator); ImGui_ImplVulkan_DestroyFontsTexture(); + if (bd->FontCommandBuffer) { vkFreeCommandBuffers(v->Device, bd->FontCommandPool, 1, &bd->FontCommandBuffer); bd->FontCommandBuffer = VK_NULL_HANDLE; } if (bd->FontCommandPool) { vkDestroyCommandPool(v->Device, bd->FontCommandPool, v->Allocator); bd->FontCommandPool = VK_NULL_HANDLE; } if (bd->ShaderModuleVert) { vkDestroyShaderModule(v->Device, bd->ShaderModuleVert, v->Allocator); bd->ShaderModuleVert = VK_NULL_HANDLE; } if (bd->ShaderModuleFrag) { vkDestroyShaderModule(v->Device, bd->ShaderModuleFrag, v->Allocator); bd->ShaderModuleFrag = VK_NULL_HANDLE; } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6f8672344..1e520da5a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -50,6 +50,7 @@ Breaking changes: Prefer using ImGuiListClipper which can return non-contiguous ranges. - Internals, Columns: commented out legacy ImGuiColumnsFlags_XXX symbols redirecting to ImGuiOldColumnsFlags_XXX, obsoleted from imgui_internal.h in 1.80. +- Commented out obsolete ImGuiKey_KeyPadEnter redirection to ImGuiKey_KeypadEnter. (#2625, #7143) Other changes: @@ -61,6 +62,7 @@ Other changes: speed asymetry when (incorrectly) attempting to scroll by non-integer amount. (#6677) - Nav, IO: SetNextFrameWantCaptureKeyboard(false) calls are not overrided back to true when navigation is enabled. SetNextFrameWantCaptureKeyboard() is always higher priority. (#6997) +- Nav: Activation can also be performed with Keypad Enter. (#5606) - Drag and Drop: Fixed drop target highlight on items temporarily pushing a widened clip rect (namely Selectables and Treenodes using SpanAllColumn flag) so the highlight properly covers all columns. (#7049, #4281, #3272) @@ -70,9 +72,11 @@ Other changes: to allow passing through multiple widgets easily. (#3092, #5759, #787) - DragScalarN, SliderScalarN, InputScalarN, PushMultiItemsWidths: improve multi-components width computation to better distribute the error. (#7120, #7121) [@Nahor] +- ColorEdit4: Layout tweaks for very small sizes. (#7120, #7121) - Menus: Tweaked hover slack logic, adding a timer to avoid situations where a slow vertical movements toward another parent BeginMenu() can keep the wrong child menu open. (#6671, #6926) - Debug Tools: Added DebugFlashStyleColor() to identify a style color. Added to Style Editor. +- Debug Tools: Debug Log: Hide its own clipper log to reduce noise in the output. - Misc: Added IMGUI_USER_H_FILENAME to change the path included when using IMGUI_INCLUDE_IMGUI_USER_H. (#7039) [@bryceberger] - Misc: Rework debug display of texture id in Metrics window to avoid compile-error when @@ -82,8 +86,12 @@ Other changes: like most printf implementations. (#7016, #3466, #6846) [@codefrog2002] - Misc: Renamed some defines in imstb_textedit.h to avoid conflicts when using unity/jumbo builds on a codebase where another copy of the library is used. +- Backends: GLFW, Emscripten: Added ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback() to + register canvas selector and auto-resize GLFW window. (#6751) [@Traveller23, @ypujante] - Backends: Vulkan: Fixed mismatching allocator passed to vkCreateCommandPool() vs vkDestroyCommandPool(). (#7075) [@FoonTheRaccoon] +- Examples: GLFW+Emscripten: Fixed examples not consistently resizing according to host canvas. + (#6751) [@Traveller23, @ypujante] ----------------------------------------------------------------------- diff --git a/examples/example_emscripten_wgpu/main.cpp b/examples/example_emscripten_wgpu/main.cpp index a2126bcd1..590453c15 100644 --- a/examples/example_emscripten_wgpu/main.cpp +++ b/examples/example_emscripten_wgpu/main.cpp @@ -76,6 +76,7 @@ int main(int, char**) // Setup Platform/Renderer backends ImGui_ImplGlfw_InitForOther(window, true); + ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback("#canvas"); ImGui_ImplWGPU_Init(wgpu_device, 3, wgpu_preferred_fmt, WGPUTextureFormat_Undefined); // Load Fonts diff --git a/examples/example_glfw_opengl3/Makefile.emscripten b/examples/example_glfw_opengl3/Makefile.emscripten index 8ea4eacfc..bd972abff 100644 --- a/examples/example_glfw_opengl3/Makefile.emscripten +++ b/examples/example_glfw_opengl3/Makefile.emscripten @@ -59,7 +59,7 @@ endif CPPFLAGS += -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends #CPPFLAGS += -g CPPFLAGS += -Wall -Wformat -Os $(EMS) -# LDFLAGS += --shell-file ../libs/emscripten/shell_minimal.html +LDFLAGS += --shell-file ../libs/emscripten/shell_minimal.html LDFLAGS += $(EMS) ##--------------------------------------------------------------------- diff --git a/examples/example_glfw_opengl3/main.cpp b/examples/example_glfw_opengl3/main.cpp index 2e369fbfe..4aaf40646 100644 --- a/examples/example_glfw_opengl3/main.cpp +++ b/examples/example_glfw_opengl3/main.cpp @@ -96,6 +96,9 @@ int main(int, char**) // Setup Platform/Renderer backends ImGui_ImplGlfw_InitForOpenGL(window, true); +#ifdef __EMSCRIPTEN__ + ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback("#canvas"); +#endif ImGui_ImplOpenGL3_Init(glsl_version); // Load Fonts diff --git a/examples/example_sdl2_directx11/main.cpp b/examples/example_sdl2_directx11/main.cpp index 69d4da11c..70fc5f849 100644 --- a/examples/example_sdl2_directx11/main.cpp +++ b/examples/example_sdl2_directx11/main.cpp @@ -47,6 +47,12 @@ int main(int, char**) // Setup window SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+DirectX11 example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); + if (window == nullptr) + { + printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError()); + return -1; + } + SDL_SysWMinfo wmInfo; SDL_VERSION(&wmInfo.version); SDL_GetWindowWMInfo(window, &wmInfo); diff --git a/examples/example_sdl2_opengl2/main.cpp b/examples/example_sdl2_opengl2/main.cpp index f4f1f2138..f92c54ebd 100644 --- a/examples/example_sdl2_opengl2/main.cpp +++ b/examples/example_sdl2_opengl2/main.cpp @@ -41,6 +41,12 @@ int main(int, char**) SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+OpenGL example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); + if (window == nullptr) + { + printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError()); + return -1; + } + SDL_GLContext gl_context = SDL_GL_CreateContext(window); SDL_GL_MakeCurrent(window, gl_context); SDL_GL_SetSwapInterval(1); // Enable vsync diff --git a/examples/example_sdl2_opengl3/main.cpp b/examples/example_sdl2_opengl3/main.cpp index e298e0a41..9f983d956 100644 --- a/examples/example_sdl2_opengl3/main.cpp +++ b/examples/example_sdl2_opengl3/main.cpp @@ -68,6 +68,12 @@ int main(int, char**) SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+OpenGL3 example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); + if (window == nullptr) + { + printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError()); + return -1; + } + SDL_GLContext gl_context = SDL_GL_CreateContext(window); SDL_GL_MakeCurrent(window, gl_context); SDL_GL_SetSwapInterval(1); // Enable vsync diff --git a/examples/example_sdl2_sdlrenderer2/main.cpp b/examples/example_sdl2_sdlrenderer2/main.cpp index bc7603eeb..bfba8fa6a 100644 --- a/examples/example_sdl2_sdlrenderer2/main.cpp +++ b/examples/example_sdl2_sdlrenderer2/main.cpp @@ -38,6 +38,11 @@ int main(int, char**) // Create window with SDL_Renderer graphics context SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+SDL_Renderer example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); + if (window == nullptr) + { + printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError()); + return -1; + } SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED); if (renderer == nullptr) { diff --git a/examples/example_sdl2_vulkan/main.cpp b/examples/example_sdl2_vulkan/main.cpp index cbaa34bc4..017f65887 100644 --- a/examples/example_sdl2_vulkan/main.cpp +++ b/examples/example_sdl2_vulkan/main.cpp @@ -385,6 +385,11 @@ int main(int, char**) // Create window with Vulkan graphics context SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+Vulkan example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); + if (window == nullptr) + { + printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError()); + return -1; + } ImVector extensions; uint32_t extensions_count = 0; diff --git a/imgui.cpp b/imgui.cpp index e3bca371f..268cee613 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -432,9 +432,10 @@ CODE - likewise io.MousePos and GetMousePos() will use OS coordinates. If you query mouse positions to interact with non-imgui coordinates you will need to offset them, e.g. subtract GetWindowViewport()->Pos. - - 2023/12/06 (1.90.1) - removed CalcListClipping() marked obsolete in 1.86. Prefer using ImGuiListClipper which can return non-contiguous ranges. - - 2023/12/05 (1.90.1) - imgui_freetype: commented out ImGuiFreeType::BuildFontAtlas() obsoleted in 1.81. prefer using #define IMGUI_ENABLE_FREETYPE or see commented code for manual calls. - - 2023/12/05 (1.90.1) - internals,columns: commented out legacy ImGuiColumnsFlags_XXX symbols redirecting to ImGuiOldColumnsFlags_XXX, obsoleted from imgui_internal.h in 1.80. + - 2023/12/19 (1.90.1) - commented out obsolete ImGuiKey_KeyPadEnter redirection to ImGuiKey_KeypadEnter. + - 2023/11/06 (1.90.1) - removed CalcListClipping() marked obsolete in 1.86. Prefer using ImGuiListClipper which can return non-contiguous ranges. + - 2023/11/05 (1.90.1) - imgui_freetype: commented out ImGuiFreeType::BuildFontAtlas() obsoleted in 1.81. prefer using #define IMGUI_ENABLE_FREETYPE or see commented code for manual calls. + - 2023/11/05 (1.90.1) - internals,columns: commented out legacy ImGuiColumnsFlags_XXX symbols redirecting to ImGuiOldColumnsFlags_XXX, obsoleted from imgui_internal.h in 1.80. - 2023/11/09 (1.90.0) - removed IM_OFFSETOF() macro in favor of using offsetof() available in C++11. Kept redirection define (will obsolete). - 2023/11/07 (1.90.0) - removed BeginChildFrame()/EndChildFrame() in favor of using BeginChild() with the ImGuiChildFlags_FrameStyle flag. kept inline redirection function (will obsolete). those functions were merely PushStyle/PopStyle helpers, the removal isn't so much motivated by needing to add the feature in BeginChild(), but by the necessity to avoid BeginChildFrame() signature mismatching BeginChild() signature and features. @@ -4982,7 +4983,7 @@ void ImGui::NewFrame() g.DebugLocateId = 0; if (g.DebugLogClipperAutoDisableFrames > 0 && --g.DebugLogClipperAutoDisableFrames == 0) { - DebugLog("(Auto-disabled ImGuiDebugLogFlags_EventClipper to avoid spamming)\n"); + DebugLog("(Debug Log: Auto-disabled ImGuiDebugLogFlags_EventClipper after 2 frames)\n"); g.DebugLogFlags &= ~ImGuiDebugLogFlags_EventClipper; } @@ -6825,7 +6826,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Add to focus scope stack // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow() - PushFocusScope(window->ID); + if ((flags & ImGuiWindowFlags_NavFlattened) == 0) + PushFocusScope(window->ID); window->NavRootFocusScopeId = g.CurrentFocusScopeId; g.CurrentWindow = NULL; @@ -7586,7 +7588,8 @@ void ImGui::End() EndColumns(); if (!(window->Flags & ImGuiWindowFlags_DockNodeHost)) // Pop inner window clip rectangle PopClipRect(); - PopFocusScope(); + if ((window->Flags & ImGuiWindowFlags_NavFlattened) == 0) + PopFocusScope(); // Stop logging if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging @@ -8194,7 +8197,7 @@ void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const I window->HitTestHoleOffset = ImVec2ih(pos - window->Pos); } -void ImGui::SetWindowHiddendAndSkipItemsForCurrentFrame(ImGuiWindow* window) +void ImGui::SetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window) { window->Hidden = window->SkipItems = true; window->HiddenFramesCanSkipItems = 1; @@ -10260,6 +10263,7 @@ void ImGuiStackSizes::CompareWithContextState(ImGuiContext* ctx) // Advance cursor given item size for layout. // Register minimum needed size so it can extend the bounding box used for auto-fit calculation. // See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different. +// THIS IS IN THE PERFORMANCE CRITICAL PATH. void ImGui::ItemSize(const ImVec2& size, float text_baseline_y) { ImGuiContext& g = *GImGui; @@ -10299,6 +10303,7 @@ void ImGui::ItemSize(const ImVec2& size, float 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. +// THIS IS IN THE PERFORMANCE CRITICAL PATH (UNTIL THE CLIPPING TEST AND EARLY-RETURN) bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemFlags extra_flags) { ImGuiContext& g = *GImGui; @@ -10313,11 +10318,11 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None; // Note: we don't copy 'g.NextItemData.SelectionUserData' to an hypothetical g.LastItemData.SelectionUserData: since the former is not cleared. - // Directional navigation processing if (id != 0) { KeepAliveID(id); + // Directional navigation processing // 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 @@ -10329,18 +10334,16 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere. if (!(g.LastItemData.InFlags & ImGuiItemFlags_NoNav)) { + // FIMXE-NAV: investigate changing the window tests into a simple 'if (g.NavFocusScopeId == g.CurrentFocusScopeId)' test. window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent); if (g.NavId == id || g.NavAnyRequest) if (g.NavWindow->RootWindowForNav == window->RootWindowForNav) if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened)) NavProcessItem(); } - - // [DEBUG] People keep stumbling on this problem and using "" as identifier in the root of a window instead of "##something". - // Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use "##something". - // READ THE FAQ: https://dearimgui.com/faq - IM_ASSERT(id != window->ID && "Cannot have an empty ID at the root of a window. If you need an empty label, use ## and read the FAQ about how the ID Stack works!"); } + + // Lightweight clear of SetNextItemXXX data. g.NextItemData.Flags = ImGuiNextItemDataFlags_None; g.NextItemData.ItemFlags = ImGuiItemFlags_None; @@ -10350,7 +10353,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu #endif // Clipping test - // (FIXME: This is a modified copy of IsClippedEx() so we can reuse the is_rect_visible value) + // (this is a modified copy of IsClippedEx() so we can reuse the is_rect_visible value) //const bool is_clipped = IsClippedEx(bb, id); //if (is_clipped) // return false; @@ -10362,12 +10365,20 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu // [DEBUG] #ifndef IMGUI_DISABLE_DEBUG_TOOLS - if (id != 0 && id == g.DebugLocateId) - DebugLocateItemResolveWithLastItem(); -#endif + if (id != 0) + { + if (id == g.DebugLocateId) + DebugLocateItemResolveWithLastItem(); + + // [DEBUG] People keep stumbling on this problem and using "" as identifier in the root of a window instead of "##something". + // Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use "##something". + // READ THE FAQ: https://dearimgui.com/faq + IM_ASSERT(id != window->ID && "Cannot have an empty ID at the root of a window. If you need an empty label, use ## and read the FAQ about how the ID Stack works!"); + } //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG] //if ((g.LastItemData.InFlags & ImGuiItemFlags_NoNav) == 0) // window->DrawList->AddRect(g.LastItemData.NavRect.Min, g.LastItemData.NavRect.Max, IM_COL32(255,255,0,255)); // [DEBUG] +#endif // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them) if (is_rect_visible) @@ -10518,10 +10529,10 @@ void ImGui::PushMultiItemsWidths(int components, float w_full) for (int i = components - 1; i > 0; i--) { float next_split = IM_TRUNC(w_items * i / components); - window->DC.ItemWidthStack.push_back(prev_split - next_split); + window->DC.ItemWidthStack.push_back(ImMax(prev_split - next_split, 1.0f)); prev_split = next_split; } - window->DC.ItemWidth = prev_split; + window->DC.ItemWidth = ImMax(prev_split, 1.0f); g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth; } @@ -11023,7 +11034,7 @@ bool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags ext if (window->Active) { // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one. - SetWindowHiddendAndSkipItemsForCurrentFrame(window); + SetWindowHiddenAndSkipItemsForCurrentFrame(window); 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_NoDocking; @@ -11971,6 +11982,8 @@ void ImGui::NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flag if ((move_flags & ImGuiNavMoveFlags_FocusApi) == 0) if (g.NavLayer != g.CurrentWindow->DC.NavLayerCurrent) return; + if (g.NavFocusScopeId != g.CurrentFocusScopeId) + return; // - Can always land on an item when using API call. // - Tabbing with _NavEnableKeyboard (space/enter/arrows): goes through every item. @@ -12317,8 +12330,8 @@ static void ImGui::NavUpdate() { const bool activate_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Space)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate)); const bool activate_pressed = activate_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Space, false)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, false))); - const bool input_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Enter)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadInput)); - const bool input_pressed = input_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Enter, false)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadInput, false))); + const bool input_down = (nav_keyboard_active && (IsKeyDown(ImGuiKey_Enter) || IsKeyDown(ImGuiKey_KeypadEnter))) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadInput)); + const bool input_pressed = input_down && ((nav_keyboard_active && (IsKeyPressed(ImGuiKey_Enter, false) || IsKeyPressed(ImGuiKey_KeypadEnter, false))) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadInput, false))); if (g.ActiveId == 0 && activate_pressed) { g.NavActivateId = g.NavId; @@ -13217,7 +13230,7 @@ bool ImGui::BeginTooltipHidden() { ImGuiContext& g = *GImGui; bool ret = Begin("##Tooltip_Hidden", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize); - SetWindowHiddendAndSkipItemsForCurrentFrame(g.CurrentWindow); + SetWindowHiddenAndSkipItemsForCurrentFrame(g.CurrentWindow); return ret; } @@ -19944,7 +19957,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) #ifdef IMGUI_DISABLE_OBSOLETE_KEYIO struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } }; #else - struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key < 512 && GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array + struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key >= 0 && key < 512 && GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array //Text("Legacy raw:"); for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key++) { if (io.KeysDown[key]) { SameLine(); Text("\"%s\" %d", GetKeyName(key), key); } } #endif Text("Keys down:"); for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !IsKeyDown(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); SameLine(); Text("(%.02f)", GetKeyData(key)->DownDuration); } @@ -20722,6 +20735,9 @@ void ImGui::ShowDebugLogWindow(bool* p_open) SetClipboardText(g.DebugLogBuf.c_str()); BeginChild("##log", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Border, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar); + const ImGuiDebugLogFlags backup_log_flags = g.DebugLogFlags; + g.DebugLogFlags &= ~ImGuiDebugLogFlags_EventClipper; + ImGuiListClipper clipper; clipper.Begin(g.DebugLogIndex.size()); while (clipper.Step()) @@ -20729,10 +20745,10 @@ void ImGui::ShowDebugLogWindow(bool* p_open) { const char* line_begin = g.DebugLogIndex.get_line_begin(g.DebugLogBuf.c_str(), line_no); const char* line_end = g.DebugLogIndex.get_line_end(g.DebugLogBuf.c_str(), line_no); - TextUnformatted(line_begin, line_end); + TextUnformatted(line_begin, line_end); // Display line ImRect text_rect = g.LastItemData.Rect; if (IsItemHovered()) - for (const char* p = line_begin; p <= line_end - 10; p++) + for (const char* p = line_begin; p <= line_end - 10; p++) // Search for 0x???????? identifiers { ImGuiID id = 0; if (p[0] != '0' || (p[1] != 'x' && p[1] != 'X') || sscanf(p + 2, "%X", &id) != 1) @@ -20745,6 +20761,7 @@ void ImGui::ShowDebugLogWindow(bool* p_open) p += 10; } } + g.DebugLogFlags = backup_log_flags; if (GetScrollY() >= GetScrollMaxY()) SetScrollHereY(1.0f); EndChild(); diff --git a/imgui.h b/imgui.h index b039df15b..4221b5765 100644 --- a/imgui.h +++ b/imgui.h @@ -1051,7 +1051,7 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_NoInputs = ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, // [Internal] - ImGuiWindowFlags_NavFlattened = 1 << 23, // [BETA] On child window: allow gamepad/keyboard navigation to cross over parent border to this child or between sibling child windows. + ImGuiWindowFlags_NavFlattened = 1 << 23, // [BETA] On child window: share focus scope, allow gamepad/keyboard navigation to cross over parent border to this child or between sibling child windows. ImGuiWindowFlags_ChildWindow = 1 << 24, // Don't use! For internal use by BeginChild() ImGuiWindowFlags_Tooltip = 1 << 25, // Don't use! For internal use by BeginTooltip() ImGuiWindowFlags_Popup = 1 << 26, // Don't use! For internal use by BeginPopup() @@ -1494,7 +1494,7 @@ enum ImGuiKey : int #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImGuiKey_ModCtrl = ImGuiMod_Ctrl, ImGuiKey_ModShift = ImGuiMod_Shift, ImGuiKey_ModAlt = ImGuiMod_Alt, ImGuiKey_ModSuper = ImGuiMod_Super, // Renamed in 1.89 - ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter, // Renamed in 1.87 + //ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter, // Renamed in 1.87 #endif }; @@ -2243,7 +2243,7 @@ struct ImGuiIO IMGUI_API void ClearEventsQueue(); // Clear all incoming events. IMGUI_API void ClearInputKeys(); // Clear current keyboard/mouse/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - IMGUI_API void ClearInputCharacters(); // [Obsolete] Clear the current frame text input buffer. Now included within ClearInputKeys(). + IMGUI_API void ClearInputCharacters(); // [Obsoleted in 1.89.8] Clear the current frame text input buffer. Now included within ClearInputKeys(). #endif //------------------------------------------------------------------ diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 43d0ad67d..cf8c9a33d 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -6172,7 +6172,7 @@ static void ShowDemoWindowInputs() struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } }; ImGuiKey start_key = ImGuiKey_NamedKey_BEGIN; #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 + struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key >= 0 && key < 512 && ImGui::GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array ImGuiKey start_key = (ImGuiKey)0; #endif ImGui::Text("Keys down:"); for (ImGuiKey key = start_key; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !ImGui::IsKeyDown(key)) continue; ImGui::SameLine(); ImGui::Text((key < ImGuiKey_NamedKey_BEGIN) ? "\"%s\"" : "\"%s\" %d", ImGui::GetKeyName(key), key); } diff --git a/imgui_internal.h b/imgui_internal.h index 72d37ea44..185de73cc 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3152,7 +3152,7 @@ namespace ImGui IMGUI_API void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond = 0); IMGUI_API void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond = 0); IMGUI_API void SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size); - IMGUI_API void SetWindowHiddendAndSkipItemsForCurrentFrame(ImGuiWindow* window); + IMGUI_API void SetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window); inline ImRect WindowRectAbsToRel(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x - off.x, r.Min.y - off.y, r.Max.x - off.x, r.Max.y - off.y); } inline ImRect WindowRectRelToAbs(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); } inline ImVec2 WindowPosRelToAbs(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x + off.x, p.y + off.y); } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 12676979d..f823aaaa9 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3931,8 +3931,8 @@ static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, Im if (c < 0x20) { bool pass = false; - 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)); + pass |= (c == '\n') && (flags & ImGuiInputTextFlags_Multiline) != 0; // Note that an Enter KEY will emit \r and be ignored (we poll for KEY in InputText() code) + pass |= (c == '\t') && (flags & ImGuiInputTextFlags_AllowTabInput) != 0; if (!pass) return false; apply_named_filters = false; // Override named filters below so newline and tabs can still be inserted. @@ -5134,10 +5134,8 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; const float square_sz = GetFrameHeight(); - const float w_full = CalcItemWidth(); - const float w_button = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x); - const float w_inputs = w_full - w_button; const char* label_display_end = FindRenderedTextEnd(label); + float w_full = CalcItemWidth(); g.NextItemData.ClearFlags(); BeginGroup(); @@ -5171,6 +5169,9 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0; const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0; const int components = alpha ? 4 : 3; + const float w_button = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x); + const float w_inputs = ImMax(w_full - w_button, 1.0f); + w_full = w_inputs + w_button; // Convert to the formats we need float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f }; @@ -5218,7 +5219,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag if (n > 0) SameLine(0, style.ItemInnerSpacing.x); float next_split = IM_TRUNC(w_items * (n + 1) / components); - SetNextItemWidth(next_split - prev_split); + SetNextItemWidth(ImMax(next_split - prev_split, 1.0f)); prev_split = next_split; // FIXME: When ImGuiColorEditFlags_HDR flag is passed HS values snap in weird ways when SV values go below 0.