From 0c5b0c8b97d5bc100e85440ee81f1d8294eac3ef Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Thu, 25 Feb 2021 14:04:07 +0200 Subject: [PATCH 01/11] Internals: Add a way to request window to not process any interactions for specified number of frames. --- backends/imgui_impl_win32.cpp | 4 ++-- imgui.cpp | 7 +++++++ imgui_internal.h | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 04e462ce..356f8469 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -134,7 +134,7 @@ bool ImGui_ImplWin32_Init(void* hwnd) break; } #endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD - + return true; } @@ -148,7 +148,7 @@ void ImGui_ImplWin32_Shutdown() g_XInputGetCapabilities = NULL; g_XInputGetState = NULL; #endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD - + g_hWnd = NULL; g_Time = 0; g_TicksPerSecond = 0; diff --git a/imgui.cpp b/imgui.cpp index 031eecf9..75675ee2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6294,6 +6294,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update the Hidden flag window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0) || (window->HiddenFramesForRenderOnly > 0); + // Disable inputs for requested number of frames + if (window->DisableInputsFrames > 0) + { + window->DisableInputsFrames--; + window->Flags |= ImGuiWindowFlags_NoInputs; + } + // Update the SkipItems flag, used to early out of all items functions (no layout required) bool skip_items = false; if (window->Collapsed || !window->Active || window->Hidden) diff --git a/imgui_internal.h b/imgui_internal.h index d01f4a6a..75e7e5e8 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1784,6 +1784,7 @@ struct IMGUI_API ImGuiWindow ImS8 HiddenFramesCanSkipItems; // Hide the window for N frames ImS8 HiddenFramesCannotSkipItems; // Hide the window for N frames while allowing items to be submitted so we can measure their size ImS8 HiddenFramesForRenderOnly; // Hide the window until frame N at Render() time only + ImS8 DisableInputsFrames; // Disable window interactions for N frames ImGuiCond SetWindowPosAllowFlags : 8; // store acceptable condition flags for SetNextWindowPos() use. ImGuiCond SetWindowSizeAllowFlags : 8; // store acceptable condition flags for SetNextWindowSize() use. ImGuiCond SetWindowCollapsedAllowFlags : 8; // store acceptable condition flags for SetNextWindowCollapsed() use. From 412d6f7efeca0539dbc9c5b005e369a43a50990b Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 18 Mar 2021 16:10:25 +0100 Subject: [PATCH 02/11] Fixes zealous MSVC static analyzers warnings (#3938) Other unfixed as I'm not happy with caving to false positives of every analyzers. --- backends/imgui_impl_win32.cpp | 1 + imgui.cpp | 11 +++++++++-- imgui_tables.cpp | 25 +++++++++++++------------ 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 356f8469..a7ccb55d 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -193,6 +193,7 @@ static bool ImGui_ImplWin32_UpdateMouseCursor() static void ImGui_ImplWin32_UpdateMousePos() { ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(g_hWnd != 0); // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) if (io.WantSetMousePos) diff --git a/imgui.cpp b/imgui.cpp index 75675ee2..52a20aed 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1133,11 +1133,18 @@ void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c) if (InputQueueSurrogate != 0) { if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate + { InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID); - else if (IM_UNICODE_CODEPOINT_MAX == (0xFFFF)) // Codepoint will not fit in ImWchar (extra parenthesis around 0xFFFF somehow fixes -Wunreachable-code with Clang) - cp = IM_UNICODE_CODEPOINT_INVALID; + } else + { +#if IM_UNICODE_CODEPOINT_MAX == 0xFFFF + cp = IM_UNICODE_CODEPOINT_INVALID; // Codepoint will not fit in ImWchar +#else cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000); +#endif + } + InputQueueSurrogate = 0; } InputQueueCharacters.push_back(cp); diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 193234cd..e8c610ad 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -2678,18 +2678,19 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table) // Write output table->SortSpecsMulti.resize(table->SortSpecsCount <= 1 ? 0 : table->SortSpecsCount); ImGuiTableColumnSortSpecs* sort_specs = (table->SortSpecsCount == 0) ? NULL : (table->SortSpecsCount == 1) ? &table->SortSpecsSingle : table->SortSpecsMulti.Data; - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - { - ImGuiTableColumn* column = &table->Columns[column_n]; - if (column->SortOrder == -1) - continue; - IM_ASSERT(column->SortOrder < table->SortSpecsCount); - ImGuiTableColumnSortSpecs* sort_spec = &sort_specs[column->SortOrder]; - sort_spec->ColumnUserID = column->UserID; - sort_spec->ColumnIndex = (ImGuiTableColumnIdx)column_n; - sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder; - sort_spec->SortDirection = column->SortDirection; - } + if (sort_specs != NULL) + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + { + ImGuiTableColumn* column = &table->Columns[column_n]; + if (column->SortOrder == -1) + continue; + IM_ASSERT(column->SortOrder < table->SortSpecsCount); + ImGuiTableColumnSortSpecs* sort_spec = &sort_specs[column->SortOrder]; + sort_spec->ColumnUserID = column->UserID; + sort_spec->ColumnIndex = (ImGuiTableColumnIdx)column_n; + sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder; + sort_spec->SortDirection = column->SortDirection; + } table->SortSpecs.Specs = sort_specs; table->SortSpecs.SpecsCount = table->SortSpecsCount; table->SortSpecs.SpecsDirty = true; // Mark as dirty for user From 61a0908713eb81de216831b89e4cfefbcd9058b3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 18 Mar 2021 18:04:38 +0100 Subject: [PATCH 03/11] Tables comments + Backends: DirectX9: calling IDirect3DStateBlock9::Capture() after CreateStateBlock() which appears to workaround/fix state restoring issues. . (#3857) --- backends/imgui_impl_dx9.cpp | 6 ++++++ docs/CHANGELOG.txt | 2 ++ imgui_tables.cpp | 28 ++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/backends/imgui_impl_dx9.cpp b/backends/imgui_impl_dx9.cpp index b8044d9a..88952c82 100644 --- a/backends/imgui_impl_dx9.cpp +++ b/backends/imgui_impl_dx9.cpp @@ -11,6 +11,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-03-18: DirectX9: Calling IDirect3DStateBlock9::Capture() after CreateStateBlock() as a workaround for state restoring issues (see #3857). // 2021-03-03: DirectX9: Added support for IMGUI_USE_BGRA_PACKED_COLOR in user's imconfig file. // 2021-02-18: DirectX9: Change blending equation to preserve alpha in output buffer. // 2019-05-29: DirectX9: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. @@ -137,6 +138,11 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) IDirect3DStateBlock9* d3d9_state_block = NULL; if (g_pd3dDevice->CreateStateBlock(D3DSBT_ALL, &d3d9_state_block) < 0) return; + if (d3d9_state_block->Capture() < 0) + { + d3d9_state_block->Release(); + return; + } // Backup the DX9 transform (DX9 documentation suggests that it is included in the StateBlock but it doesn't appear to) D3DMATRIX last_world, last_view, last_projection; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 7af96cff..1cbe7cc6 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -42,6 +42,8 @@ Other Changes: - Drags, Sliders, Inputs: Specifying a NULL format to Float functions default them to "%.3f" to be consistent with the compile-time default. (#3922) - DragScalar: Add default value for v_speed argument to match higher-level functions. (#3922) [@eliasdaler] +- Backends: DirectX9: calling IDirect3DStateBlock9::Capture() after CreateStateBlock() which appears to + workaround/fix state restoring issues. Unknown exactly why so, but bit of a cargo-cult fix. (#3857) - Docs: Improvements to minor mistakes in documentation comments (#3923) [@ANF-Studios] diff --git a/imgui_tables.cpp b/imgui_tables.cpp index e8c610ad..3bbc0a72 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -8,6 +8,7 @@ Index of this file: // [SECTION] Commentary // [SECTION] Header mess // [SECTION] Tables: Main code +// [SECTION] Tables: Simple accessors // [SECTION] Tables: Row changes // [SECTION] Tables: Columns changes // [SECTION] Tables: Columns width management @@ -235,6 +236,19 @@ Index of this file: //----------------------------------------------------------------------------- // [SECTION] Tables: Main code //----------------------------------------------------------------------------- +// - TableFixFlags() [Internal] +// - TableFindByID() [Internal] +// - BeginTable() +// - BeginTableEx() [Internal] +// - TableBeginInitMemory() [Internal] +// - TableBeginApplyRequests() [Internal] +// - TableSetupColumnFlags() [Internal] +// - TableUpdateLayout() [Internal] +// - TableUpdateBorders() [Internal] +// - EndTable() +// - TableSetupColumn() +// - TableSetupScrollFreeze() +//----------------------------------------------------------------------------- // Configuration static const int TABLE_DRAW_CHANNEL_BG0 = 0; @@ -1435,6 +1449,20 @@ void ImGui::TableSetupScrollFreeze(int columns, int rows) table->IsUnfrozenRows = (table->FreezeRowsCount == 0); // Make sure this is set before TableUpdateLayout() so ImGuiListClipper can benefit from it.b } +//----------------------------------------------------------------------------- +// [SECTION] Tables: Simple accessors +//----------------------------------------------------------------------------- +// - TableGetColumnCount() +// - TableGetColumnName() +// - TableGetColumnName() [Internal] +// - TableSetColumnEnabled() [Internal] +// - TableGetColumnFlags() +// - TableGetCellBgRect() [Internal] +// - TableGetColumnResizeID() [Internal] +// - TableGetHoveredColumn() [Internal] +// - TableSetBgColor() +//----------------------------------------------------------------------------- + int ImGui::TableGetColumnCount() { ImGuiContext& g = *GImGui; From 6e4770ea5cee412aca4ac3f9567f1a50f980bbce Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 18 Mar 2021 18:12:52 +0100 Subject: [PATCH 04/11] Examples: Vulkan: Rebuild swapchain on VK_SUBOPTIMAL_KHR. (#3881) Ref https://vulkan-tutorial.com/Drawing_a_triangle/Swap_chain_recreation#page_Suboptimal-or-out-of-date-swap-chain --- docs/CHANGELOG.txt | 1 + examples/example_glfw_vulkan/main.cpp | 4 ++-- examples/example_sdl_vulkan/main.cpp | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1cbe7cc6..6d3dd260 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -44,6 +44,7 @@ Other Changes: - DragScalar: Add default value for v_speed argument to match higher-level functions. (#3922) [@eliasdaler] - Backends: DirectX9: calling IDirect3DStateBlock9::Capture() after CreateStateBlock() which appears to workaround/fix state restoring issues. Unknown exactly why so, but bit of a cargo-cult fix. (#3857) +- Examples: Vulkan: Rebuild swapchain on VK_SUBOPTIMAL_KHR. (#3881) - Docs: Improvements to minor mistakes in documentation comments (#3923) [@ANF-Studios] diff --git a/examples/example_glfw_vulkan/main.cpp b/examples/example_glfw_vulkan/main.cpp index d2e4004e..3b1a0d51 100644 --- a/examples/example_glfw_vulkan/main.cpp +++ b/examples/example_glfw_vulkan/main.cpp @@ -253,7 +253,7 @@ static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore; VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); - if (err == VK_ERROR_OUT_OF_DATE_KHR) + if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) { g_SwapChainRebuild = true; return; @@ -326,7 +326,7 @@ 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) + if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) { g_SwapChainRebuild = true; return; diff --git a/examples/example_sdl_vulkan/main.cpp b/examples/example_sdl_vulkan/main.cpp index ea038bac..5ab8d26b 100644 --- a/examples/example_sdl_vulkan/main.cpp +++ b/examples/example_sdl_vulkan/main.cpp @@ -245,7 +245,7 @@ static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore; VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); - if (err == VK_ERROR_OUT_OF_DATE_KHR) + if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) { g_SwapChainRebuild = true; return; @@ -318,7 +318,7 @@ 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) + if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) { g_SwapChainRebuild = true; return; From 59da01901ed71a53e5578c34d90d631757c3870d Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 19 Mar 2021 15:25:43 +0100 Subject: [PATCH 05/11] Scrolling: Fix scroll tracking with e.g. SetScrollHereX/Y() when WindowPadding < ItemSpacing. Fix scroll snapping on edge of scroll region when both scrollbars are enabled. CalcNextScrollFromScrollTargetAndClamp() fixed snapping edge calculation missing ScrollbarSizes.y --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 37 ++++++++++++++++++++++--------------- imgui.h | 2 +- imgui_demo.cpp | 3 ++- imgui_widgets.cpp | 2 +- 5 files changed, 28 insertions(+), 18 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6d3dd260..b7e74a61 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -39,6 +39,8 @@ Breaking Changes: Other Changes: +- Scrolling: Fix scroll tracking with e.g. SetScrollHereX/Y() when WindowPadding < ItemSpacing. +- Scrolling: Fix scroll snapping on edge of scroll region when both scrollbars are enabled. - Drags, Sliders, Inputs: Specifying a NULL format to Float functions default them to "%.3f" to be consistent with the compile-time default. (#3922) - DragScalar: Add default value for v_speed argument to match higher-level functions. (#3922) [@eliasdaler] diff --git a/imgui.cpp b/imgui.cpp index 52a20aed..47a77b30 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5172,8 +5172,9 @@ static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& s if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize))) { ImGuiWindow* window_for_height = 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, window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows + new_size.y = ImMax(new_size.y, decoration_up_height + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows } return new_size; } @@ -5202,9 +5203,9 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont { ImGuiContext& g = *GImGui; ImGuiStyle& style = g.Style; - ImVec2 size_decorations = ImVec2(0.0f, window->TitleBarHeight() + window->MenuBarHeight()); + const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); ImVec2 size_pad = window->WindowPadding * 2.0f; - ImVec2 size_desired = size_contents + size_pad + size_decorations; + ImVec2 size_desired = size_contents + size_pad + ImVec2(0.0f, decoration_up_height); if (window->Flags & ImGuiWindowFlags_Tooltip) { // Tooltip always resize @@ -5226,8 +5227,8 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont // When the window cannot fit all contents (either because of constraints, either because screen is too small), // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding. ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit); - bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - size_decorations.x < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar); - bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - size_decorations.y < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar); + bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - 0.0f < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar); + bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - decoration_up_height < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar); if (will_have_scrollbar_x) size_auto_fit.y += style.ScrollbarSize; if (will_have_scrollbar_y) @@ -7774,24 +7775,29 @@ static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window) ImVec2 scroll = window->Scroll; if (window->ScrollTarget.x < FLT_MAX) { + float decoration_total_width = window->ScrollbarSizes.x; float center_x_ratio = window->ScrollTargetCenterRatio.x; float scroll_target_x = window->ScrollTarget.x; - float snap_x_min = 0.0f; - float snap_x_max = window->ScrollMax.x + window->Size.x; if (window->ScrollTargetEdgeSnapDist.x > 0.0f) + { + float snap_x_min = 0.0f; + float snap_x_max = window->ScrollMax.x + window->SizeFull.x - decoration_total_width; scroll_target_x = CalcScrollEdgeSnap(scroll_target_x, snap_x_min, snap_x_max, window->ScrollTargetEdgeSnapDist.x, center_x_ratio); - scroll.x = scroll_target_x - center_x_ratio * (window->SizeFull.x - window->ScrollbarSizes.x); + } + scroll.x = scroll_target_x - center_x_ratio * (window->SizeFull.x - decoration_total_width); } if (window->ScrollTarget.y < FLT_MAX) { - float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); + float decoration_total_height = window->TitleBarHeight() + window->MenuBarHeight() + window->ScrollbarSizes.y; float center_y_ratio = window->ScrollTargetCenterRatio.y; float scroll_target_y = window->ScrollTarget.y; - float snap_y_min = 0.0f; - float snap_y_max = window->ScrollMax.y + window->Size.y - decoration_up_height; if (window->ScrollTargetEdgeSnapDist.y > 0.0f) + { + float snap_y_min = 0.0f; + float snap_y_max = window->ScrollMax.y + window->SizeFull.y - decoration_total_height; scroll_target_y = CalcScrollEdgeSnap(scroll_target_y, snap_y_min, snap_y_max, window->ScrollTargetEdgeSnapDist.y, center_y_ratio); - scroll.y = scroll_target_y - center_y_ratio * (window->SizeFull.y - window->ScrollbarSizes.y - decoration_up_height); + } + scroll.y = scroll_target_y - center_y_ratio * (window->SizeFull.y - decoration_total_height); } scroll.x = IM_FLOOR(ImMax(scroll.x, 0.0f)); scroll.y = IM_FLOOR(ImMax(scroll.y, 0.0f)); @@ -7904,7 +7910,8 @@ void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio) { IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f); - local_y -= window->TitleBarHeight() + window->MenuBarHeight(); // FIXME: Would be nice to have a more standardized access to our scrollable/client rect + const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); // FIXME: Would be nice to have a more standardized access to our scrollable/client rect; + local_y -= decoration_up_height; window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y); // Convert local position to scroll offset window->ScrollTargetCenterRatio.y = center_y_ratio; window->ScrollTargetEdgeSnapDist.y = 0.0f; @@ -7927,7 +7934,7 @@ void ImGui::SetScrollHereX(float center_x_ratio) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - float spacing_x = g.Style.ItemSpacing.x; + float spacing_x = ImMax(window->WindowPadding.x, g.Style.ItemSpacing.x); float target_pos_x = ImLerp(window->DC.LastItemRect.Min.x - spacing_x, window->DC.LastItemRect.Max.x + spacing_x, center_x_ratio); SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos @@ -7940,7 +7947,7 @@ void ImGui::SetScrollHereY(float center_y_ratio) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - float spacing_y = g.Style.ItemSpacing.y; + float spacing_y = ImMax(window->WindowPadding.y, g.Style.ItemSpacing.y); float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio); SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos diff --git a/imgui.h b/imgui.h index f7dc5828..d1e2f1d6 100644 --- a/imgui.h +++ b/imgui.h @@ -61,7 +61,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.83 WIP" -#define IMGUI_VERSION_NUM 18201 +#define IMGUI_VERSION_NUM 18202 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 0db22d85..5ccee4f7 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2838,6 +2838,8 @@ static void ShowDemoWindowLayout() { for (int item = 0; item < 100; item++) { + if (item > 0) + ImGui::SameLine(); if (enable_track && item == track_item) { ImGui::TextColored(ImVec4(1, 1, 0, 1), "Item %d", item); @@ -2847,7 +2849,6 @@ static void ShowDemoWindowLayout() { ImGui::Text("Item %d", item); } - ImGui::SameLine(); } } float scroll_x = ImGui::GetScrollX(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 22e17b54..e9548c0c 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6594,7 +6594,7 @@ void ImGui::EndMenuBar() IM_ASSERT(window->DC.MenuBarAppending); PopClipRect(); PopID(); - window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->MenuBarRect().Min.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos. + window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->Pos.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos. g.GroupStack.back().EmitItem = false; EndGroup(); // Restore position on layer 0 window->DC.LayoutType = ImGuiLayoutType_Vertical; From 6d3a980f38d849e9eac22180dce12f4c2e16794c Mon Sep 17 00:00:00 2001 From: John Asper Date: Sun, 21 Mar 2021 12:29:24 -0400 Subject: [PATCH 06/11] Backends: Vulkan: Fix mapped memory validation error when buffer sizes are not multiple of VkPhysicalDeviceLimits::nonCoherentAtomSize. (#3957) If using Vulkan backend and either vertex_size or index_size is not aligned to VkPhysicalDeviceLimits::nonCoherentAtomSize, then the call to vkFlushMappedMemoryRanges in ImGui_ImplVulkan_RenderDrawData will result to validation error. --- backends/imgui_impl_vulkan.cpp | 7 ++++--- docs/CHANGELOG.txt | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index d8c00e9b..167687ed 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -22,6 +22,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-03-22: Vulkan: Fix mapped memory validation error when buffer sizes are not multiple of VkPhysicalDeviceLimits::nonCoherentAtomSize. // 2021-02-18: Vulkan: Change blending equation to preserve alpha in output buffer. // 2021-01-27: Vulkan: Added support for custom function load and IMGUI_IMPL_VULKAN_NO_PROTOTYPES by using ImGui_ImplVulkan_LoadFunctions(). // 2020-11-11: Vulkan: Added support for specifying which subpass to reference during VkPipeline creation. @@ -343,7 +344,7 @@ static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory err = vkBindBufferMemory(v->Device, buffer, buffer_memory, 0); check_vk_result(err); - p_buffer_size = new_size; + p_buffer_size = req.size; } static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline pipeline, VkCommandBuffer command_buffer, ImGui_ImplVulkanH_FrameRenderBuffers* rb, int fb_width, int fb_height) @@ -429,9 +430,9 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm // Upload vertex/index data into a single contiguous GPU buffer ImDrawVert* vtx_dst = NULL; ImDrawIdx* idx_dst = NULL; - VkResult err = vkMapMemory(v->Device, rb->VertexBufferMemory, 0, vertex_size, 0, (void**)(&vtx_dst)); + VkResult err = vkMapMemory(v->Device, rb->VertexBufferMemory, 0, rb->VertexBufferSize, 0, (void**)(&vtx_dst)); check_vk_result(err); - err = vkMapMemory(v->Device, rb->IndexBufferMemory, 0, index_size, 0, (void**)(&idx_dst)); + err = vkMapMemory(v->Device, rb->IndexBufferMemory, 0, rb->IndexBufferSize, 0, (void**)(&idx_dst)); check_vk_result(err); for (int n = 0; n < draw_data->CmdListsCount; n++) { diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index b7e74a61..b18879e8 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -46,6 +46,8 @@ Other Changes: - DragScalar: Add default value for v_speed argument to match higher-level functions. (#3922) [@eliasdaler] - Backends: DirectX9: calling IDirect3DStateBlock9::Capture() after CreateStateBlock() which appears to workaround/fix state restoring issues. Unknown exactly why so, but bit of a cargo-cult fix. (#3857) +- Backends: Vulkan: Fix mapped memory Vulkan validation error when buffer sizes are not multiple of + VkPhysicalDeviceLimits::nonCoherentAtomSize. (#3957) [@AgentX1994] - Examples: Vulkan: Rebuild swapchain on VK_SUBOPTIMAL_KHR. (#3881) - Docs: Improvements to minor mistakes in documentation comments (#3923) [@ANF-Studios] From 186b734db0c090fba109569b569dc1507616dbe0 Mon Sep 17 00:00:00 2001 From: Nikolai Wuttke Date: Sat, 20 Mar 2021 15:06:53 +0100 Subject: [PATCH 07/11] Backends: SDL2: Extend global mouse pos availability check (#3950) --- backends/imgui_impl_sdl.cpp | 13 ++++++++++--- docs/CHANGELOG.txt | 2 ++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 7ab70c57..d3cd35af 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -17,6 +17,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-03-22: Rework global mouse pos availability check listing supported platforms explicitly, effectively fixing mouse access on Raspberry Pi. (#2837, #3950) // 2020-05-25: Misc: Report a zero display-size when window is minimized, to be consistent with other backends. // 2020-02-20: Inputs: Fixed mapping for ImGuiKey_KeyPadEnter (using SDL_SCANCODE_KP_ENTER instead of SDL_SCANCODE_RETURN2). // 2019-12-17: Inputs: On Wayland, use SDL_GetMouseState (because there is no global mouse state). @@ -177,8 +178,14 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window) g_MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); g_MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO); - // Check and store if we are on Wayland - g_MouseCanUseGlobalState = strncmp(SDL_GetCurrentVideoDriver(), "wayland", 7) != 0; + // Check and store if we are on a SDL backend that supports global mouse position + // ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list) + const char* sdl_backend = SDL_GetCurrentVideoDriver(); + const char* global_mouse_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" }; + g_MouseCanUseGlobalState = false; + for (int n = 0; n < IM_ARRAYSIZE(global_mouse_whitelist); n++) + if (strncmp(sdl_backend, global_mouse_whitelist[n], strlen(global_mouse_whitelist[n])) == 0) + g_MouseCanUseGlobalState = true; #ifdef _WIN32 SDL_SysWMinfo wmInfo; @@ -259,7 +266,7 @@ static void ImGui_ImplSDL2_UpdateMousePosAndButtons() { // SDL_GetMouseState() gives mouse position seemingly based on the last window entered/focused(?) // The creation of a new windows at runtime and SDL_CaptureMouse both seems to severely mess up with that, so we retrieve that position globally. - // Won't use this workaround when on Wayland, as there is no global mouse position. + // Won't use this workaround on SDL backends that have no global mouse position, like Wayland or RPI int wx, wy; SDL_GetWindowPosition(focused_window, &wx, &wy); SDL_GetGlobalMouseState(&mx, &my); diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index b18879e8..220ae812 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -44,6 +44,8 @@ Other Changes: - Drags, Sliders, Inputs: Specifying a NULL format to Float functions default them to "%.3f" to be consistent with the compile-time default. (#3922) - DragScalar: Add default value for v_speed argument to match higher-level functions. (#3922) [@eliasdaler] +- Backends: SDL: Rework global mouse pos availability check listing supported platforms explicitly, + effectively fixing mouse access on Raspberry Pi. (#2837, #3950) [@lethal-guitar, @hinxx] - Backends: DirectX9: calling IDirect3DStateBlock9::Capture() after CreateStateBlock() which appears to workaround/fix state restoring issues. Unknown exactly why so, but bit of a cargo-cult fix. (#3857) - Backends: Vulkan: Fix mapped memory Vulkan validation error when buffer sizes are not multiple of From 1491d2c9168a1982ea88dd638509e64ec990daf7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 23 Mar 2021 16:48:10 +0100 Subject: [PATCH 08/11] Backends: Win32: Clearing keyboard down array when losing focus (WM_KILLFOCUS). (#2062, #3532, #3961) --- backends/imgui_impl_win32.cpp | 4 ++++ docs/CHANGELOG.txt | 2 ++ 2 files changed, 6 insertions(+) diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index a7ccb55d..edb7c3c5 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -32,6 +32,7 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-03-23: Inputs: Clearing keyboard down array when losing focus (WM_KILLFOCUS). // 2021-02-18: Added ImGui_ImplWin32_EnableAlphaCompositing(). Non Visual Studio users will need to link with dwmapi.lib (MinGW/gcc: use -ldwmapi). // 2021-02-17: Fixed ImGui_ImplWin32_EnableDpiAwareness() attempting to get SetProcessDpiAwareness from shcore.dll on Windows 8 whereas it is only supported on Windows 8.1. // 2021-01-25: Inputs: Dynamically loading XInput DLL. @@ -373,6 +374,9 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA if (wParam < 256) io.KeysDown[wParam] = 0; return 0; + case WM_KILLFOCUS: + memset(io.KeysDown, 0, sizeof(io.KeysDown)); + return 0; case WM_CHAR: // You can also use ToAscii()+GetKeyboardState() to retrieve characters. if (wParam > 0 && wParam < 0x10000) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 220ae812..a6e9fd32 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -46,6 +46,8 @@ Other Changes: - DragScalar: Add default value for v_speed argument to match higher-level functions. (#3922) [@eliasdaler] - Backends: SDL: Rework global mouse pos availability check listing supported platforms explicitly, effectively fixing mouse access on Raspberry Pi. (#2837, #3950) [@lethal-guitar, @hinxx] +- Backends: Win32: Clearing keyboard down array when losing focus (WM_KILLFOCUS). (#2062, #3532, #3961) + [@1025798851] - Backends: DirectX9: calling IDirect3DStateBlock9::Capture() after CreateStateBlock() which appears to workaround/fix state restoring issues. Unknown exactly why so, but bit of a cargo-cult fix. (#3857) - Backends: Vulkan: Fix mapped memory Vulkan validation error when buffer sizes are not multiple of From 6f360d60401f36a1d27570edb5657053bceaf22d Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 23 Mar 2021 18:09:21 +0100 Subject: [PATCH 09/11] Examples: Win32: using a more explicit loop for PeekMessage polling to make the code easier to copy and paste and less error-prone. --- examples/example_sdl_directx11/main.cpp | 2 +- examples/example_win32_directx10/main.cpp | 13 ++++++++----- examples/example_win32_directx11/main.cpp | 13 ++++++++----- examples/example_win32_directx12/main.cpp | 13 ++++++++----- examples/example_win32_directx9/main.cpp | 13 ++++++++----- 5 files changed, 33 insertions(+), 21 deletions(-) diff --git a/examples/example_sdl_directx11/main.cpp b/examples/example_sdl_directx11/main.cpp index 46da1c59..c5ad42e3 100644 --- a/examples/example_sdl_directx11/main.cpp +++ b/examples/example_sdl_directx11/main.cpp @@ -103,7 +103,7 @@ int main(int, char**) if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) done = true; if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED && event.window.windowID == SDL_GetWindowID(window)) - { + { // Release all outstanding references to the swap chain's buffers before resizing. CleanupRenderTarget(); g_pSwapChain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, 0); diff --git a/examples/example_win32_directx10/main.cpp b/examples/example_win32_directx10/main.cpp index b1360018..ad88d1e9 100644 --- a/examples/example_win32_directx10/main.cpp +++ b/examples/example_win32_directx10/main.cpp @@ -78,21 +78,24 @@ int main(int, char**) ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); // Main loop - MSG msg; - ZeroMemory(&msg, sizeof(msg)); - while (msg.message != WM_QUIT) + bool done = false; + while (!done) { // Poll and handle messages (inputs, window resize, etc.) // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. - if (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) + MSG msg; + while (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); - continue; + if (msg.message == WM_QUIT) + done = true; } + if (done) + break; // Start the Dear ImGui frame ImGui_ImplDX10_NewFrame(); diff --git a/examples/example_win32_directx11/main.cpp b/examples/example_win32_directx11/main.cpp index e882f6a1..7f1b4b89 100644 --- a/examples/example_win32_directx11/main.cpp +++ b/examples/example_win32_directx11/main.cpp @@ -78,21 +78,24 @@ int main(int, char**) ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); // Main loop - MSG msg; - ZeroMemory(&msg, sizeof(msg)); - while (msg.message != WM_QUIT) + bool done = false; + while (!done) { // Poll and handle messages (inputs, window resize, etc.) // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. - if (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) + MSG msg; + while (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); - continue; + if (msg.message == WM_QUIT) + done = true; } + if (done) + break; // Start the Dear ImGui frame ImGui_ImplDX11_NewFrame(); diff --git a/examples/example_win32_directx12/main.cpp b/examples/example_win32_directx12/main.cpp index 0ce990a8..8476fe7d 100644 --- a/examples/example_win32_directx12/main.cpp +++ b/examples/example_win32_directx12/main.cpp @@ -117,21 +117,24 @@ int main(int, char**) ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); // Main loop - MSG msg; - ZeroMemory(&msg, sizeof(msg)); - while (msg.message != WM_QUIT) + bool done = false; + while (!done) { // Poll and handle messages (inputs, window resize, etc.) // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. - if (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) + MSG msg; + while (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); - continue; + if (msg.message == WM_QUIT) + done = true; } + if (done) + break; // Start the Dear ImGui frame ImGui_ImplDX12_NewFrame(); diff --git a/examples/example_win32_directx9/main.cpp b/examples/example_win32_directx9/main.cpp index ce59a9a8..bdcd8b3e 100644 --- a/examples/example_win32_directx9/main.cpp +++ b/examples/example_win32_directx9/main.cpp @@ -76,21 +76,24 @@ int main(int, char**) ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); // Main loop - MSG msg; - ZeroMemory(&msg, sizeof(msg)); - while (msg.message != WM_QUIT) + bool done = false; + while (!done) { // Poll and handle messages (inputs, window resize, etc.) // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. - if (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) + MSG msg; + while (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); - continue; + if (msg.message == WM_QUIT) + done = true; } + if (done) + break; // Start the Dear ImGui frame ImGui_ImplDX9_NewFrame(); From aa5431fde2e3f534c64cc254bfd92f4a14fb46ee Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 24 Mar 2021 14:33:28 +0100 Subject: [PATCH 10/11] Tables: Expose TableSetColumnEnabled() in public api. (#3935) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 2 +- imgui.h | 1 + imgui_internal.h | 1 - imgui_tables.cpp | 3 +++ 5 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a6e9fd32..36c1b405 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,6 +41,7 @@ Other Changes: - Scrolling: Fix scroll tracking with e.g. SetScrollHereX/Y() when WindowPadding < ItemSpacing. - Scrolling: Fix scroll snapping on edge of scroll region when both scrollbars are enabled. +- Tables: Expose TableSetColumnEnabled() in public api. (#3935) - Drags, Sliders, Inputs: Specifying a NULL format to Float functions default them to "%.3f" to be consistent with the compile-time default. (#3922) - DragScalar: Add default value for v_speed argument to match higher-level functions. (#3922) [@eliasdaler] diff --git a/imgui.cpp b/imgui.cpp index 47a77b30..14c9c9bb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7091,7 +7091,7 @@ static void ImGui::ErrorCheckNewFrameSanityChecks() ImGuiContext& g = *GImGui; // Check user IM_ASSERT macro - // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means you assert macro is incorrectly defined! + // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means your assert macro is incorrectly defined! // If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block. // This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.) // #define IM_ASSERT(EXPR) if (SomeCode(EXPR)) SomeMoreCode(); // Wrong! diff --git a/imgui.h b/imgui.h index d1e2f1d6..4867414f 100644 --- a/imgui.h +++ b/imgui.h @@ -723,6 +723,7 @@ namespace ImGui IMGUI_API int TableGetRowIndex(); // return current row index. IMGUI_API const char* TableGetColumnName(int column_n = -1); // return "" if column didn't have a name declared by TableSetupColumn(). Pass -1 to use current column. IMGUI_API ImGuiTableColumnFlags TableGetColumnFlags(int column_n = -1); // return column flags so you can query their Enabled/Visible/Sorted/Hovered status flags. Pass -1 to use current column. + IMGUI_API void TableSetColumnEnabled(int column_n, bool v);// change enabled/disabled state of a column, set to false to hide the column. Note that end-user can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody) IMGUI_API void TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n = -1); // change the color of a cell, row, or column. See ImGuiTableBgTarget_ flags for details. // Legacy Columns API (2020: prefer using Tables!) diff --git a/imgui_internal.h b/imgui_internal.h index 75e7e5e8..041b6f30 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2361,7 +2361,6 @@ namespace ImGui // Tables: Candidates for public API IMGUI_API void TableOpenContextMenu(int column_n = -1); - IMGUI_API void TableSetColumnEnabled(int column_n, bool enabled); IMGUI_API void TableSetColumnWidth(int column_n, float width); IMGUI_API void TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs); IMGUI_API int TableGetHoveredColumn(); // May use (TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered) instead. Return hovered column. return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered. diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 3bbc0a72..c01c7081 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1491,6 +1491,9 @@ const char* ImGui::TableGetColumnName(const ImGuiTable* table, int column_n) return &table->ColumnsNames.Buf[column->NameOffset]; } +// Request enabling/disabling a column (often perceived as "showing/hiding" from users point of view) +// Note that end-user can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody) +// Request will be applied during next layout, which happens on the first call to TableNextRow() after BeginTable() // For the getter you can use (TableGetColumnFlags() & ImGuiTableColumnFlags_IsEnabled) void ImGui::TableSetColumnEnabled(int column_n, bool enabled) { From a58271c079e1e47b6913d8f611d6f219f0a1399f Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 25 Mar 2021 16:16:48 +0100 Subject: [PATCH 11/11] Internals: extracted a more reusable BeginViewportSideBar() out of BeginMainMenuBar(). (#3966, #3518) Complement ca34c81c in docking branch which removed assumption that we can't tell size ahead of Begin(). --- imgui.cpp | 6 ++-- imgui_internal.h | 22 ++++++++----- imgui_widgets.cpp | 79 ++++++++++++++++++++++++++++------------------- 3 files changed, 66 insertions(+), 41 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 14c9c9bb..b899447b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10566,9 +10566,9 @@ static void ImGui::UpdateViewportsNewFrame() ImGuiViewportP* viewport = g.Viewports[n]; // Lock down space taken by menu bars and status bars, reset the offset for fucntions like BeginMainMenuBar() to alter them again. - viewport->WorkOffsetMin = viewport->CurrWorkOffsetMin; - viewport->WorkOffsetMax = viewport->CurrWorkOffsetMax; - viewport->CurrWorkOffsetMin = viewport->CurrWorkOffsetMax = ImVec2(0.0f, 0.0f); + viewport->WorkOffsetMin = viewport->BuildWorkOffsetMin; + viewport->WorkOffsetMax = viewport->BuildWorkOffsetMax; + viewport->BuildWorkOffsetMin = viewport->BuildWorkOffsetMax = ImVec2(0.0f, 0.0f); viewport->UpdateWorkRect(); } } diff --git a/imgui_internal.h b/imgui_internal.h index 041b6f30..7ad22310 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1180,14 +1180,21 @@ struct ImGuiViewportP : public ImGuiViewport ImVec2 WorkOffsetMin; // Work Area: Offset from Pos to top-left corner of Work Area. Generally (0,0) or (0,+main_menu_bar_height). Work Area is Full Area but without menu-bars/status-bars (so WorkArea always fit inside Pos/Size!) ImVec2 WorkOffsetMax; // Work Area: Offset from Pos+Size to bottom-right corner of Work Area. Generally (0,0) or (0,-status_bar_height). - ImVec2 CurrWorkOffsetMin; // Work Area: Offset being built/increased during current frame - ImVec2 CurrWorkOffsetMax; // Work Area: Offset being built/decreased during current frame + 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]); } - ImRect GetMainRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } - ImRect GetWorkRect() const { return ImRect(WorkPos.x, WorkPos.y, WorkPos.x + WorkSize.x, WorkPos.y + WorkSize.y); } - void UpdateWorkRect() { WorkPos = ImVec2(Pos.x + WorkOffsetMin.x, Pos.y + WorkOffsetMin.y); WorkSize = ImVec2(ImMax(0.0f, Size.x - WorkOffsetMin.x + WorkOffsetMax.x), ImMax(0.0f, Size.y - WorkOffsetMin.y + WorkOffsetMax.y)); } + 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]); } + + // 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); } + ImVec2 CalcWorkRectSize(const ImVec2& off_min, const ImVec2& off_max) const { return ImVec2(ImMax(0.0f, Size.x - off_min.x + off_max.x), ImMax(0.0f, Size.y - off_min.y + off_max.y)); } + void UpdateWorkRect() { WorkPos = CalcWorkRectPos(WorkOffsetMin); WorkSize = CalcWorkRectSize(WorkOffsetMin, WorkOffsetMax); } // Update public fields + + // Helpers to retrieve ImRect (we don't need to store BuildWorkRect as every access tend to change it, hence the code asymmetry) + ImRect GetMainRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } + ImRect GetWorkRect() const { return ImRect(WorkPos.x, WorkPos.y, WorkPos.x + WorkSize.x, WorkPos.y + WorkSize.y); } + ImRect GetBuildWorkRect() const { ImVec2 pos = CalcWorkRectPos(BuildWorkOffsetMin); ImVec2 size = CalcWorkRectSize(BuildWorkOffsetMin, BuildWorkOffsetMax); return ImRect(pos.x, pos.y, pos.x + size.x, pos.y + size.y); } }; //----------------------------------------------------------------------------- @@ -2309,6 +2316,7 @@ namespace ImGui IMGUI_API ImGuiWindow* GetTopMostPopupModal(); IMGUI_API ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window); IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy); + IMGUI_API bool BeginViewportSideBar(const char* name, ImGuiViewport* viewport, ImGuiDir dir, float size, ImGuiWindowFlags window_flags); // Gamepad/Keyboard Navigation IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index e9548c0c..8c358c59 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6602,46 +6602,63 @@ void ImGui::EndMenuBar() window->DC.MenuBarAppending = false; } +// Important: calling order matters! +// FIXME: Somehow overlapping with docking tech. +// FIXME: The "rect-cut" aspect of this could be formalized into a lower-level helper (rect-cut: https://halt.software/dead-simple-layouts) +bool ImGui::BeginViewportSideBar(const char* name, ImGuiViewport* viewport_p, ImGuiDir dir, float axis_size, ImGuiWindowFlags window_flags) +{ + IM_ASSERT(dir != ImGuiDir_None); + + ImGuiWindow* bar_window = FindWindowByName(name); + 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; + if (dir == ImGuiDir_Right || dir == ImGuiDir_Down) + pos[axis] = avail_rect.Max[axis] - axis_size; + ImVec2 size = avail_rect.GetSize(); + size[axis] = axis_size; + SetNextWindowPos(pos); + SetNextWindowSize(size); + + // Report our size into work area (for next frame) using actual window size + if (dir == ImGuiDir_Up || dir == ImGuiDir_Left) + viewport->BuildWorkOffsetMin[axis] += axis_size; + else if (dir == ImGuiDir_Down || dir == ImGuiDir_Right) + viewport->BuildWorkOffsetMax[axis] -= axis_size; + } + + window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; + PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); // Lift normal size constraint + bool is_open = Begin(name, NULL, window_flags); + PopStyleVar(2); + + return is_open; +} + bool ImGui::BeginMainMenuBar() { ImGuiContext& g = *GImGui; ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport(); - ImGuiWindow* menu_bar_window = FindWindowByName("##MainMenuBar"); // For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set. + // 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. g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f)); - - // Get our rectangle at the top of the work area - if (menu_bar_window == NULL || menu_bar_window->BeginCount == 0) - { - // Set window position - // We don't attempt to calculate our height ahead, as it depends on the per-viewport font size. - // However menu-bar will affect the minimum window size so we'll get the right height. - ImVec2 menu_bar_pos = viewport->Pos + viewport->CurrWorkOffsetMin; - ImVec2 menu_bar_size = ImVec2(viewport->Size.x - viewport->CurrWorkOffsetMin.x + viewport->CurrWorkOffsetMax.x, 1.0f); - SetNextWindowPos(menu_bar_pos); - SetNextWindowSize(menu_bar_size); - } - - // Create window - PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); // Lift normal size constraint, however the presence of a menu-bar will give us the minimum height we want. - ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; - bool is_open = Begin("##MainMenuBar", NULL, window_flags) && BeginMenuBar(); - PopStyleVar(2); - - // Report our size into work area (for next frame) using actual window size - menu_bar_window = GetCurrentWindow(); - if (menu_bar_window->BeginCount == 1) - viewport->CurrWorkOffsetMin.y += menu_bar_window->Size.y; - + ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; + float height = GetFrameHeight(); + bool is_open = BeginViewportSideBar("##MainMenuBar", viewport, ImGuiDir_Up, height, window_flags); g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f); - if (!is_open) - { + + if (is_open) + BeginMenuBar(); + else End(); - return false; - } - return true; //-V1020 + return is_open; } void ImGui::EndMainMenuBar()