From df89a16d265c3f02f403afefc0e60b5d227d94fb Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 7 Aug 2020 15:34:25 +0200 Subject: [PATCH 1/8] Examples: Vulkan: Reworked buffer resize handling, fix for Linux/X11. (#3390, #2626) --- docs/CHANGELOG.txt | 1 + examples/example_glfw_vulkan/main.cpp | 18 ++++++++---------- examples/example_sdl_vulkan/main.cpp | 19 ++++++++----------- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 3f2a0474..a25f469c 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -51,6 +51,7 @@ Other Changes: (This is also necessary to support full multi/range-select/drag and drop operations.) - Metrics: Various tweaks, listing windows front-to-back, greying inactive items when possible. - Demo: Add simple InputText() callbacks demo (aside from the more elaborate ones in 'Examples->Console'). +- Examples: Vulkan: Reworked buffer resize handling, fix for Linux/X11. (#3390, #2626) [@RoryO] ----------------------------------------------------------------------- diff --git a/examples/example_glfw_vulkan/main.cpp b/examples/example_glfw_vulkan/main.cpp index 58d0ab7f..8e75abbf 100644 --- a/examples/example_glfw_vulkan/main.cpp +++ b/examples/example_glfw_vulkan/main.cpp @@ -310,7 +310,7 @@ static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) } } -static void FramePresent(ImGui_ImplVulkanH_Window* wd) +static void FramePresent(ImGui_ImplVulkanH_Window* wd, GLFWwindow* window) { VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; VkPresentInfoKHR info = {}; @@ -321,6 +321,12 @@ static void FramePresent(ImGui_ImplVulkanH_Window* wd) info.pSwapchains = &wd->Swapchain; info.pImageIndices = &wd->FrameIndex; VkResult err = vkQueuePresentKHR(g_Queue, &info); + if (err == VK_ERROR_OUT_OF_DATE_KHR) + { + glfwGetFramebufferSize(window, &g_SwapChainResizeWidth, &g_SwapChainResizeHeight); + g_SwapChainRebuild = true; + return; + } check_vk_result(err); wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->ImageCount; // Now we can use the next set of semaphores } @@ -330,13 +336,6 @@ static void glfw_error_callback(int error, const char* description) fprintf(stderr, "Glfw Error %d: %s\n", error, description); } -static void glfw_resize_callback(GLFWwindow*, int w, int h) -{ - g_SwapChainRebuild = true; - g_SwapChainResizeWidth = w; - g_SwapChainResizeHeight = h; -} - int main(int, char**) { // Setup GLFW window @@ -365,7 +364,6 @@ int main(int, char**) // Create Framebuffers int w, h; glfwGetFramebufferSize(window, &w, &h); - glfwSetFramebufferSizeCallback(window, glfw_resize_callback); ImGui_ImplVulkanH_Window* wd = &g_MainWindowData; SetupVulkanWindow(wd, surface, w, h); @@ -515,7 +513,7 @@ int main(int, char**) { memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); FrameRender(wd, draw_data); - FramePresent(wd); + FramePresent(wd, window); } } diff --git a/examples/example_sdl_vulkan/main.cpp b/examples/example_sdl_vulkan/main.cpp index 6de7afab..c0a98088 100644 --- a/examples/example_sdl_vulkan/main.cpp +++ b/examples/example_sdl_vulkan/main.cpp @@ -302,7 +302,7 @@ static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) } } -static void FramePresent(ImGui_ImplVulkanH_Window* wd) +static void FramePresent(ImGui_ImplVulkanH_Window* wd, SDL_Window* window) { VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; VkPresentInfoKHR info = {}; @@ -313,6 +313,12 @@ static void FramePresent(ImGui_ImplVulkanH_Window* wd) info.pSwapchains = &wd->Swapchain; info.pImageIndices = &wd->FrameIndex; VkResult err = vkQueuePresentKHR(g_Queue, &info); + if (err == VK_ERROR_OUT_OF_DATE_KHR) + { + SDL_GetWindowSize(window, &g_SwapChainResizeWidth, &g_SwapChainResizeHeight); + g_SwapChainRebuild = true; + return; + } check_vk_result(err); wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->ImageCount; // Now we can use the next set of semaphores } @@ -445,15 +451,6 @@ int main(int, char**) ImGui_ImplSDL2_ProcessEvent(&event); if (event.type == SDL_QUIT) done = true; - if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED && event.window.windowID == SDL_GetWindowID(window)) - { - // Note: your own application may rely on SDL_WINDOWEVENT_MINIMIZED/SDL_WINDOWEVENT_RESTORED to skip updating all-together. - // Here ImGui_ImplSDL2_NewFrame() will set io.DisplaySize to zero which will disable rendering but let application run. - // Please note that you can't Present into a minimized window. - g_SwapChainResizeWidth = (int)event.window.data1; - g_SwapChainResizeHeight = (int)event.window.data2; - g_SwapChainRebuild = true; - } } // Resize swap chain? @@ -515,7 +512,7 @@ int main(int, char**) { memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); FrameRender(wd, draw_data); - FramePresent(wd); + FramePresent(wd, window); } } From fdf952108dd92897fabcba59b834a2ce9241b4ed Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 21 Aug 2020 19:16:59 +0200 Subject: [PATCH 2/8] Drags, Sliders: internal ReadOnly flag gets forwarded properly to temp InputText(). --- imgui_internal.h | 2 +- imgui_widgets.cpp | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 999641ae..ec5bde09 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2006,7 +2006,7 @@ namespace ImGui // InputText IMGUI_API bool InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); IMGUI_API bool TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags); - IMGUI_API bool TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min = NULL, const void* p_clamp_max = NULL); + IMGUI_API bool TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min = NULL, const void* p_clamp_max = NULL, ImGuiInputTextFlags flags = 0); inline bool TempInputIsActive(ImGuiID id) { ImGuiContext& g = *GImGui; return (g.ActiveId == id && g.TempInputId == id); } inline ImGuiInputTextState* GetInputTextState(ImGuiID id) { ImGuiContext& g = *GImGui; return (g.InputTextState.ID == id) ? &g.InputTextState : NULL; } // Get input text state if active diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 3f929f32..a0810a18 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -2230,7 +2230,8 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, { // Only clamp CTRL+Click input when ImGuiSliderFlags_ClampInput is set const bool is_clamp_input = (flags & ImGuiSliderFlags_ClampOnInput) != 0 && (p_min == NULL || p_max == NULL || DataTypeCompare(data_type, p_min, p_max) < 0); - return TempInputScalar(frame_bb, id, label, data_type, p_data, format, is_clamp_input ? p_min : NULL, is_clamp_input ? p_max : NULL); + const bool is_readonly = (flags & ImGuiSliderFlags_ReadOnly) != 0 && (window->DC.ItemFlags & ImGuiItemFlags_ReadOnly) != 0; + return TempInputScalar(frame_bb, id, label, data_type, p_data, format, is_clamp_input ? p_min : NULL, is_clamp_input ? p_max : NULL, is_readonly ? ImGuiInputTextFlags_ReadOnly : 0); } // Draw frame @@ -2833,7 +2834,8 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat { // Only clamp CTRL+Click input when ImGuiSliderFlags_ClampInput is set const bool is_clamp_input = (flags & ImGuiSliderFlags_ClampOnInput) != 0; - return TempInputScalar(frame_bb, id, label, data_type, p_data, format, is_clamp_input ? p_min : NULL, is_clamp_input ? p_max : NULL); + const bool is_readonly = (flags & ImGuiSliderFlags_ReadOnly) != 0 && (window->DC.ItemFlags & ImGuiItemFlags_ReadOnly) != 0; + return TempInputScalar(frame_bb, id, label, data_type, p_data, format, is_clamp_input ? p_min : NULL, is_clamp_input ? p_max : NULL, is_readonly ? ImGuiInputTextFlags_ReadOnly : 0); } // Draw frame @@ -3164,7 +3166,7 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* // ImGuiSliderFlags_ClampOnInput / ImGuiSliderFlags_ClampOnInput 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. -bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min, const void* p_clamp_max) +bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min, const void* p_clamp_max, ImGuiInputTextFlags flags) { ImGuiContext& g = *GImGui; @@ -3174,8 +3176,9 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, p_data, format); ImStrTrimBlanks(data_buf); - ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoMarkEdited; + flags |= ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoMarkEdited; flags |= ((data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) ? ImGuiInputTextFlags_CharsScientific : ImGuiInputTextFlags_CharsDecimal); + bool value_changed = false; if (TempInputText(bb, id, label, data_buf, IM_ARRAYSIZE(data_buf), flags)) { From 7b0570d6bac96ed64d379d240907f8fee319d57b Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 21 Aug 2020 20:15:07 +0200 Subject: [PATCH 3/8] Revert "Drags, Sliders: internal ReadOnly flag gets forwarded properly to temp InputText()." This reverts commit 640d1f60ce140e4c2bf858ac2f2e8a96d432e6a4. --- imgui_internal.h | 2 +- imgui_widgets.cpp | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index ec5bde09..999641ae 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2006,7 +2006,7 @@ namespace ImGui // InputText IMGUI_API bool InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); IMGUI_API bool TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags); - IMGUI_API bool TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min = NULL, const void* p_clamp_max = NULL, ImGuiInputTextFlags flags = 0); + IMGUI_API bool TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min = NULL, const void* p_clamp_max = NULL); inline bool TempInputIsActive(ImGuiID id) { ImGuiContext& g = *GImGui; return (g.ActiveId == id && g.TempInputId == id); } inline ImGuiInputTextState* GetInputTextState(ImGuiID id) { ImGuiContext& g = *GImGui; return (g.InputTextState.ID == id) ? &g.InputTextState : NULL; } // Get input text state if active diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index a0810a18..3f929f32 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -2230,8 +2230,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, { // Only clamp CTRL+Click input when ImGuiSliderFlags_ClampInput is set const bool is_clamp_input = (flags & ImGuiSliderFlags_ClampOnInput) != 0 && (p_min == NULL || p_max == NULL || DataTypeCompare(data_type, p_min, p_max) < 0); - const bool is_readonly = (flags & ImGuiSliderFlags_ReadOnly) != 0 && (window->DC.ItemFlags & ImGuiItemFlags_ReadOnly) != 0; - return TempInputScalar(frame_bb, id, label, data_type, p_data, format, is_clamp_input ? p_min : NULL, is_clamp_input ? p_max : NULL, is_readonly ? ImGuiInputTextFlags_ReadOnly : 0); + return TempInputScalar(frame_bb, id, label, data_type, p_data, format, is_clamp_input ? p_min : NULL, is_clamp_input ? p_max : NULL); } // Draw frame @@ -2834,8 +2833,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat { // Only clamp CTRL+Click input when ImGuiSliderFlags_ClampInput is set const bool is_clamp_input = (flags & ImGuiSliderFlags_ClampOnInput) != 0; - const bool is_readonly = (flags & ImGuiSliderFlags_ReadOnly) != 0 && (window->DC.ItemFlags & ImGuiItemFlags_ReadOnly) != 0; - return TempInputScalar(frame_bb, id, label, data_type, p_data, format, is_clamp_input ? p_min : NULL, is_clamp_input ? p_max : NULL, is_readonly ? ImGuiInputTextFlags_ReadOnly : 0); + return TempInputScalar(frame_bb, id, label, data_type, p_data, format, is_clamp_input ? p_min : NULL, is_clamp_input ? p_max : NULL); } // Draw frame @@ -3166,7 +3164,7 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* // ImGuiSliderFlags_ClampOnInput / ImGuiSliderFlags_ClampOnInput 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. -bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min, const void* p_clamp_max, ImGuiInputTextFlags flags) +bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min, const void* p_clamp_max) { ImGuiContext& g = *GImGui; @@ -3176,9 +3174,8 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, p_data, format); ImStrTrimBlanks(data_buf); - flags |= ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoMarkEdited; + ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoMarkEdited; flags |= ((data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) ? ImGuiInputTextFlags_CharsScientific : ImGuiInputTextFlags_CharsDecimal); - bool value_changed = false; if (TempInputText(bb, id, label, data_buf, IM_ARRAYSIZE(data_buf), flags)) { From d451f6cc30d83e1dcb49c10ffe57b4f881b30de4 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 24 Aug 2020 14:13:18 +0200 Subject: [PATCH 4/8] Nav tweaks. Demo: Fixed drag and drop demo state (broken by f152fac4f1). Fixed incorrect format string (which would work without IMGUI_DISABLE_OBSOLETE_FUNCTIONS). --- imgui.cpp | 15 ++++++++++----- imgui_demo.cpp | 4 ++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 797d0a08..53864293 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3622,16 +3622,17 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow. // - When moving a window we can skip the search, which also conveniently bypasses the fact that window->WindowRectClipped is lagging as this point of the frame. // - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms. + bool clear_hovered_windows = false; FindHoveredWindow(); // Modal windows prevents mouse from hovering behind them. ImGuiWindow* modal_window = GetTopMostPopupModal(); if (modal_window && g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window)) - g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL; + clear_hovered_windows = true; // Disabled mouse? if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse) - g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL; + clear_hovered_windows = true; // We track click ownership. When clicked outside of a window the click is owned by the application and won't report hovering nor request capture even while dragging over our windows afterward. int mouse_earliest_button_down = -1; @@ -3651,6 +3652,9 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02) const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0; if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload) + clear_hovered_windows = true; + + if (clear_hovered_windows) g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL; // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app) @@ -9116,10 +9120,11 @@ static void ImGui::NavUpdateWindowing() if (move_delta.x != 0.0f || move_delta.y != 0.0f) { const float NAV_MOVE_SPEED = 800.0f; - const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't code variable framerate very well - SetWindowPos(g.NavWindowingTarget->RootWindow, g.NavWindowingTarget->RootWindow->Pos + move_delta * move_speed, ImGuiCond_Always); + const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't handle variable framerate very well + ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow; + SetWindowPos(moving_window, moving_window->Pos + move_delta * move_speed, ImGuiCond_Always); + MarkIniSettingsDirty(moving_window); g.NavDisableMouseHover = true; - MarkIniSettingsDirty(g.NavWindowingTarget); } } diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 625bf1aa..efb83a4d 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1575,7 +1575,7 @@ static void ShowDemoWindowWidgets() static int slider_i = 50; ImGui::Text("Underlying float value: %f", slider_f); ImGui::SliderFloat("SliderFloat (0 -> 1)", &slider_f, 0.0f, 1.0f, "%.3f", flags); - ImGui::SliderInt("SliderInt (0 -> 100)", &slider_i, 0, 100, "%.3f", flags); + ImGui::SliderInt("SliderInt (0 -> 100)", &slider_i, 0, 100, "%d", flags); ImGui::TreePop(); } @@ -1820,7 +1820,7 @@ static void ShowDemoWindowWidgets() if (ImGui::RadioButton("Copy", mode == Mode_Copy)) { mode = Mode_Copy; } ImGui::SameLine(); if (ImGui::RadioButton("Move", mode == Mode_Move)) { mode = Mode_Move; } ImGui::SameLine(); if (ImGui::RadioButton("Swap", mode == Mode_Swap)) { mode = Mode_Swap; } - const char* names[9] = + static const char* names[9] = { "Bobby", "Beatrice", "Betty", "Brianna", "Barry", "Bernard", From 2e50d0706bf18ee2fe21cf8eb7dc680f64a6fea0 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 24 Aug 2020 16:26:47 +0200 Subject: [PATCH 5/8] Selectable: Tweaks. Added internal ImGuiSelectableFlags_NoPadWithHalfSpacing. --- imgui_internal.h | 13 +++++++------ imgui_widgets.cpp | 47 +++++++++++++++++++++++++---------------------- 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 999641ae..b4083d2b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -642,12 +642,13 @@ enum ImGuiSliderFlagsPrivate_ enum ImGuiSelectableFlagsPrivate_ { // NB: need to be in sync with last value of ImGuiSelectableFlags_ - ImGuiSelectableFlags_NoHoldingActiveID = 1 << 20, - ImGuiSelectableFlags_SelectOnClick = 1 << 21, // Override button behavior to react on Click (default is Click+Release) - ImGuiSelectableFlags_SelectOnRelease = 1 << 22, // Override button behavior to react on Release (default is Click+Release) - ImGuiSelectableFlags_SpanAvailWidth = 1 << 23, // Span all avail width even if we declared less for layout purpose. FIXME: We may be able to remove this (added in 6251d379, 2bcafc86 for menus) - ImGuiSelectableFlags_DrawHoveredWhenHeld= 1 << 24, // Always show active when held, even is not hovered. This concept could probably be renamed/formalized somehow. - ImGuiSelectableFlags_SetNavIdOnHover = 1 << 25 + ImGuiSelectableFlags_NoHoldingActiveID = 1 << 20, + ImGuiSelectableFlags_SelectOnClick = 1 << 21, // Override button behavior to react on Click (default is Click+Release) + ImGuiSelectableFlags_SelectOnRelease = 1 << 22, // Override button behavior to react on Release (default is Click+Release) + ImGuiSelectableFlags_SpanAvailWidth = 1 << 23, // Span all avail width even if we declared less for layout purpose. FIXME: We may be able to remove this (added in 6251d379, 2bcafc86 for menus) + ImGuiSelectableFlags_DrawHoveredWhenHeld = 1 << 24, // Always show active when held, even is not hovered. This concept could probably be renamed/formalized somehow. + ImGuiSelectableFlags_SetNavIdOnHover = 1 << 25, // Set Nav/Focus ID on mouse hover (used by MenuItem) + ImGuiSelectableFlags_NoPadWithHalfSpacing = 1 << 26 // Disable padding each side with ItemSpacing * 0.5f }; // Extend ImGuiTreeNodeFlags_ diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 3f929f32..2ae40d98 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5889,7 +5889,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; - if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.CurrentColumns) // FIXME-OPT: Avoid if vertically clipped. + const bool span_all_columns = (flags & ImGuiSelectableFlags_SpanAllColumns) != 0; + if (span_all_columns && window->DC.CurrentColumns) // FIXME-OPT: Avoid if vertically clipped. PushColumnsBackground(); // Submit label or explicit size to ItemSize(), whereas ItemAdd() will submit a larger/spanning rectangle. @@ -5901,8 +5902,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl ItemSize(size, 0.0f); // Fill horizontal space - const float min_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? window->ParentWorkRect.Min.x : pos.x; - const float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x; + const float min_x = span_all_columns ? window->ParentWorkRect.Min.x : pos.x; + const float max_x = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x; if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_SpanAvailWidth)) size.x = ImMax(label_size.x, max_x - min_x); @@ -5911,33 +5912,35 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl const ImVec2 text_max(min_x + size.x, pos.y + size.y); // Selectables are meant to be tightly packed together with no click-gap, so we extend their box to cover spacing between selectable. - ImRect bb_enlarged(min_x, pos.y, text_max.x, text_max.y); - const float spacing_x = style.ItemSpacing.x; - const float spacing_y = style.ItemSpacing.y; - const float spacing_L = IM_FLOOR(spacing_x * 0.50f); - const float spacing_U = IM_FLOOR(spacing_y * 0.50f); - bb_enlarged.Min.x -= spacing_L; - bb_enlarged.Min.y -= spacing_U; - bb_enlarged.Max.x += (spacing_x - spacing_L); - bb_enlarged.Max.y += (spacing_y - spacing_U); - //if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb_align.Min, bb_align.Max, IM_COL32(255, 0, 0, 255)); } - //if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb_enlarged.Min, bb_enlarged.Max, IM_COL32(0, 255, 0, 255)); } + ImRect bb(min_x, pos.y, text_max.x, text_max.y); + if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0) + { + const float spacing_x = style.ItemSpacing.x; + const float spacing_y = style.ItemSpacing.y; + const float spacing_L = IM_FLOOR(spacing_x * 0.50f); + const float spacing_U = IM_FLOOR(spacing_y * 0.50f); + bb.Min.x -= spacing_L; + bb.Min.y -= spacing_U; + bb.Max.x += (spacing_x - spacing_L); + bb.Max.y += (spacing_y - spacing_U); + } + //if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(0, 255, 0, 255)); } bool item_add; if (flags & ImGuiSelectableFlags_Disabled) { ImGuiItemFlags backup_item_flags = window->DC.ItemFlags; window->DC.ItemFlags |= ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNavDefaultFocus; - item_add = ItemAdd(bb_enlarged, id); + item_add = ItemAdd(bb, id); window->DC.ItemFlags = backup_item_flags; } else { - item_add = ItemAdd(bb_enlarged, id); + item_add = ItemAdd(bb, id); } if (!item_add) { - if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.CurrentColumns) + if (span_all_columns && window->DC.CurrentColumns) PopColumnsBackground(); return false; } @@ -5956,7 +5959,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl const bool was_selected = selected; bool hovered, held; - bool pressed = ButtonBehavior(bb_enlarged, id, &hovered, &held, button_flags); + bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with gamepad/keyboard if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover))) @@ -5983,15 +5986,15 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (hovered || selected) { const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); - RenderFrame(bb_enlarged.Min, bb_enlarged.Max, col, false, 0.0f); - RenderNavHighlight(bb_enlarged, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); + RenderFrame(bb.Min, bb.Max, col, false, 0.0f); + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); } - if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.CurrentColumns) + if (span_all_columns && window->DC.CurrentColumns) PopColumnsBackground(); if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); - RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb_enlarged); + RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb); if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor(); // Automatically close popups From 08108cf9ee352b9e868d3d7b59ece88b4d4a6d90 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 25 Aug 2020 11:49:16 +0200 Subject: [PATCH 6/8] Tab Bar: Hide tab item close button while dragging a tab. --- docs/CHANGELOG.txt | 1 + docs/TODO.txt | 1 - imgui_widgets.cpp | 5 +++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a25f469c..5092f144 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -49,6 +49,7 @@ Other Changes: rather than the Mouse Down+Up sequence, even if the _OpenOnArrow flag isn't set. This is standard behavior and amends the change done in 1.76 which only affected cases were _OpenOnArrow flag was set. (This is also necessary to support full multi/range-select/drag and drop operations.) +- Tab Bar: Hide tab item close button while dragging a tab. - Metrics: Various tweaks, listing windows front-to-back, greying inactive items when possible. - Demo: Add simple InputText() callbacks demo (aside from the more elaborate ones in 'Examples->Console'). - Examples: Vulkan: Reworked buffer resize handling, fix for Linux/X11. (#3390, #2626) [@RoryO] diff --git a/docs/TODO.txt b/docs/TODO.txt index ca026df0..e1b1f3e3 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -169,7 +169,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - tabs: "there is currently a problem because TabItem() will try to submit their own tooltip after 0.50 second, and this will have the effect of making your tooltip flicker once." -> tooltip priority work - tabs: close button tends to overlap unsaved-document star - tabs: consider showing the star at the same spot as the close button, like VS Code does. - - tabs: while dragging/reordering a tab, close button decoration shouldn't appear on other tabs - tabs: make EndTabBar fail if users doesn't respect BeginTabBar return value, for consistency/future-proofing. - tabs: persistent order/focus in BeginTabBar() api (#261, #351) - tabs: TabItem could honor SetNextItemWidth()? diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 2ae40d98..6dd2eff1 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7453,7 +7453,8 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, SetItemAllowOverlap(); // Drag and drop: re-order tabs - if (held && !tab_appearing && IsMouseDragging(0)) + const bool is_dragging = (held && !tab_appearing && IsMouseDragging(0)); + if (is_dragging) { if (!g.DragDropActive && (tab_bar->Flags & ImGuiTabBarFlags_Reorderable)) { @@ -7496,7 +7497,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; // Render tab label, process close button - const ImGuiID close_button_id = p_open ? window->GetID((void*)((intptr_t)id + 1)) : 0; + const ImGuiID close_button_id = (p_open && !is_dragging) ? window->GetID((void*)((intptr_t)id + 1)) : 0; bool just_closed = TabItemLabelAndCloseButton(display_draw_list, bb, flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible); if (just_closed && p_open != NULL) { From 021c28ae39594b0d6dc78ab8581f35cb8776012c Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Tue, 25 Aug 2020 13:13:03 +0300 Subject: [PATCH 7/8] Nav: Fix ScrollToBringRectIntoView() not bringing entire item into view when nav moves to the left. Correct some comments. --- imgui.cpp | 2 +- imgui_internal.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 53864293..bd25ed1c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7392,7 +7392,7 @@ ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_ if (!window_rect.Contains(item_rect)) { if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x) - SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x + g.Style.ItemSpacing.x, 0.0f); + SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x - g.Style.ItemSpacing.x, 0.0f); else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x) SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f); if (item_rect.Min.y < window_rect.Min.y) diff --git a/imgui_internal.h b/imgui_internal.h index b4083d2b..2cb5dfa3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1191,11 +1191,11 @@ struct ImGuiContext ImGuiKeyModFlags NavJustMovedToKeyMods; ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame. ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS WILL ONLY BE None or NavGamepad or NavKeyboard. - ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->DC.NavRefRectRel[], modified for directional navigation scoring. + ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring. int NavScoringCount; // Metrics for debugging ImGuiNavLayer NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later. int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing - bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRefRectRel is valid + bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRectRel is valid bool NavMousePosDirty; // When set we will update mouse position if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) if set (NB: this not enabled by default) bool NavDisableHighlight; // When user starts using mouse, we hide gamepad/keyboard highlight (NB: but they are still available, which is why NavDisableHighlight isn't always != NavDisableMouseHover) bool NavDisableMouseHover; // When user starts using gamepad/keyboard, we hide mouse hovering highlight until mouse is touched again. From 5919a6fa89e077e0b94f93036a2cef690790b06b Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 25 Aug 2020 19:28:20 +0200 Subject: [PATCH 8/8] Tab Bar: Keep tab item close button visible while dragging a tab (independent of hovering state). Improve 08108cf --- docs/CHANGELOG.txt | 2 +- imgui_widgets.cpp | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 5092f144..573b813a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -49,7 +49,7 @@ Other Changes: rather than the Mouse Down+Up sequence, even if the _OpenOnArrow flag isn't set. This is standard behavior and amends the change done in 1.76 which only affected cases were _OpenOnArrow flag was set. (This is also necessary to support full multi/range-select/drag and drop operations.) -- Tab Bar: Hide tab item close button while dragging a tab. +- Tab Bar: Keep tab item close button visible while dragging a tab (independent of hovering state). - Metrics: Various tweaks, listing windows front-to-back, greying inactive items when possible. - Demo: Add simple InputText() callbacks demo (aside from the more elaborate ones in 'Examples->Console'). - Examples: Vulkan: Reworked buffer resize handling, fix for Linux/X11. (#3390, #2626) [@RoryO] diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 6dd2eff1..2ee4c191 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7453,8 +7453,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, SetItemAllowOverlap(); // Drag and drop: re-order tabs - const bool is_dragging = (held && !tab_appearing && IsMouseDragging(0)); - if (is_dragging) + if (held && !tab_appearing && IsMouseDragging(0)) { if (!g.DragDropActive && (tab_bar->Flags & ImGuiTabBarFlags_Reorderable)) { @@ -7497,7 +7496,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 && !is_dragging) ? window->GetID((void*)((intptr_t)id + 1)) : 0; + const ImGuiID close_button_id = p_open ? window->GetID((void*)((intptr_t)id + 1)) : 0; bool just_closed = TabItemLabelAndCloseButton(display_draw_list, bb, flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible); if (just_closed && p_open != NULL) { @@ -7609,7 +7608,7 @@ bool ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, bool close_button_visible = false; if (close_button_id != 0) if (is_contents_visible || bb.GetWidth() >= g.Style.TabMinWidthForUnselectedCloseButton) - if (g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == close_button_id) + if (g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == tab_id || g.ActiveId == close_button_id) close_button_visible = true; if (close_button_visible) {