From 770f9daab3a81524147bd3252f14322ae2f52bec Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 16 Apr 2021 18:27:48 +0200 Subject: [PATCH 1/5] Tables: Better preserve column data (mainly widths) when columns count changes. (#4046) + .ini skips columns with no data. --- docs/CHANGELOG.txt | 1 + imgui.h | 2 +- imgui_tables.cpp | 33 +++++++++++++++++++++++++-------- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a1a4d7ef..6e497633 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -44,6 +44,7 @@ Other Changes: - Scrolling: Fix mouse wheel axis swap when using SHIFT on macOS (system already does it). (#4010) - Window: Fix IsWindowAppearing() from returning true twice in most cases. (#3982, #1497, #1061) - Tables: Expose TableSetColumnEnabled() in public api. (#3935) +- Tables: Better preserve widths when columns count changes. (#4046) - TabBar: Fixed mouse reordering with very fast movements (e.g. crossing multiple tabs in a single frame and then immediately standling still (would only affect automation/bots). [@rokups] - Drags, Sliders, Inputs: Specifying a NULL format to Float functions default them to "%.3f" to be diff --git a/imgui.h b/imgui.h index 1caf9323..44b9922a 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 18203 +#define IMGUI_VERSION_NUM 18204 #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_tables.cpp b/imgui_tables.cpp index c01c7081..3677f9e4 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -470,10 +470,14 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->MemoryCompacted = false; // Setup memory buffer (clear data if columns count changed) - const int stored_size = table->Columns.size(); - if (stored_size != 0 && stored_size != columns_count) + ImGuiTableColumn* old_columns_to_preserve = NULL; + void* old_columns_raw_data = NULL; + const int old_columns_count = table->Columns.size(); + if (old_columns_count != 0 && old_columns_count != columns_count) { - IM_FREE(table->RawData); + // Attempt to preserve width on column count change (#4046) + old_columns_to_preserve = table->Columns.Data; + old_columns_raw_data = table->RawData; table->RawData = NULL; } if (table->RawData == NULL) @@ -496,14 +500,24 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG for (int n = 0; n < columns_count; n++) { ImGuiTableColumn* column = &table->Columns[n]; - float width_auto = column->WidthAuto; - *column = ImGuiTableColumn(); - column->WidthAuto = width_auto; - column->IsPreserveWidthAuto = true; // Preserve WidthAuto when reinitializing a live table: not technically necessary but remove a visible flicker + if (old_columns_to_preserve && n < old_columns_count) + { + // FIXME: We don't attempt to preserve column order in this path. + *column = old_columns_to_preserve[n]; + } + else + { + float width_auto = column->WidthAuto; + *column = ImGuiTableColumn(); + column->WidthAuto = width_auto; + column->IsPreserveWidthAuto = true; // Preserve WidthAuto when reinitializing a live table: not technically necessary but remove a visible flicker + column->IsEnabled = column->IsEnabledNextFrame = true; + } column->DisplayOrder = table->DisplayOrderToIndex[n] = (ImGuiTableColumnIdx)n; - column->IsEnabled = column->IsEnabledNextFrame = true; } } + if (old_columns_raw_data) + IM_FREE(old_columns_raw_data); // Load settings if (table->IsSettingsRequestLoad) @@ -3350,6 +3364,9 @@ static void TableSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandle for (int column_n = 0; column_n < settings->ColumnsCount; column_n++, column++) { // "Column 0 UserID=0x42AD2D21 Width=100 Visible=1 Order=0 Sort=0v" + bool save_column = column->UserID != 0 || save_size || save_visible || save_order || (save_sort && column->SortOrder != -1); + if (!save_column) + continue; buf->appendf("Column %-2d", column_n); if (column->UserID != 0) buf->appendf(" UserID=%08X", column->UserID); if (save_size && column->IsStretch) buf->appendf(" Weight=%.4f", column->WidthOrWeight); From 936f53229db4d45b445a3c26795718523262654e Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Mar 2021 17:08:04 +0100 Subject: [PATCH 2/5] Internals: maintaining focus order inside windows + only storing root windows in WindowsFocusOrder[] array. (toward #2304) --- imgui.cpp | 50 +++++++++++++++++++++++++++--------------------- imgui_internal.h | 7 ++++--- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 6ce2b950..3215b9b0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4034,7 +4034,7 @@ void ImGui::NewFrame() UpdateTabFocus(); // Mark all windows as not visible and compact unused memory. - IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size); + IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size); const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer; for (int i = 0; i != g.Windows.Size; i++) { @@ -5143,7 +5143,12 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0); } - g.WindowsFocusOrder.push_back(window); + if (!(flags & ImGuiWindowFlags_ChildWindow)) + { + g.WindowsFocusOrder.push_back(window); + window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1); + } + if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus) g.Windows.push_front(window); // Quite slow but rare and only once else @@ -6378,15 +6383,22 @@ void ImGui::End() void ImGui::BringWindowToFocusFront(ImGuiWindow* window) { ImGuiContext& g = *GImGui; + IM_ASSERT(window == window->RootWindow); + + const int cur_order = window->FocusOrder; + IM_ASSERT(g.WindowsFocusOrder[cur_order] == window); if (g.WindowsFocusOrder.back() == window) return; - for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the top-most window - if (g.WindowsFocusOrder[i] == window) - { - memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*)); - g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window; - break; - } + + const int new_order = g.WindowsFocusOrder.Size - 1; + for (int n = cur_order; n < new_order; n++) + { + g.WindowsFocusOrder[n] = g.WindowsFocusOrder[n + 1]; + g.WindowsFocusOrder[n]->FocusOrder--; + IM_ASSERT(g.WindowsFocusOrder[n]->FocusOrder == n); + } + g.WindowsFocusOrder[new_order] = window; + window->FocusOrder = (short)new_order; } void ImGui::BringWindowToDisplayFront(ImGuiWindow* window) @@ -6466,18 +6478,13 @@ void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWind { ImGuiContext& g = *GImGui; - int start_idx = g.WindowsFocusOrder.Size - 1; - if (under_this_window != NULL) - { - int under_this_window_idx = FindWindowFocusIndex(under_this_window); - if (under_this_window_idx != -1) - start_idx = under_this_window_idx - 1; - } + const int start_idx = ((under_this_window != NULL) ? FindWindowFocusIndex(under_this_window) : g.WindowsFocusOrder.Size) - 1; for (int i = start_idx; i >= 0; i--) { // We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user. ImGuiWindow* window = g.WindowsFocusOrder[i]; - if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow)) + IM_ASSERT(window == window->RootWindow); + if (window != ignore_window && window->WasActive) if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) { ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window); @@ -9479,13 +9486,12 @@ static void ImGui::NavEndFrame() } } -static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N) +static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) { ImGuiContext& g = *GImGui; - for (int i = g.WindowsFocusOrder.Size - 1; i >= 0; i--) - if (g.WindowsFocusOrder[i] == window) - return i; - return -1; + int order = window->FocusOrder; + IM_ASSERT(g.WindowsFocusOrder[order] == window); + return order; } static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N) diff --git a/imgui_internal.h b/imgui_internal.h index c100cfc9..a8c2b440 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1322,7 +1322,7 @@ struct ImGuiContext // Windows state ImVector Windows; // Windows, sorted in display order, back to front - ImVector WindowsFocusOrder; // Windows, sorted in focus order, back to front. (FIXME: We could only store root windows here! Need to sort out the Docking equivalent which is RootWindowDockStop and is unfortunately a little more dynamic) + ImVector WindowsFocusOrder; // Root windows, sorted in focus order, back to front. ImVector WindowsTempSortBuffer; // Temporary buffer used in EndFrame() to reorder windows so parents are kept before their child ImVector CurrentWindowStack; ImGuiStorage WindowsById; // Map window's ImGuiID to ImGuiWindow* @@ -1786,8 +1786,9 @@ struct IMGUI_API ImGuiWindow bool HasCloseButton; // Set when the window has a close button (p_open != NULL) signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3) short BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) - short BeginOrderWithinParent; // Order within immediate parent window, if we are a child window. Otherwise 0. - short BeginOrderWithinContext; // Order within entire imgui context. This is mostly used for debugging submission order related issues. + short BeginOrderWithinParent; // Begin() order within immediate parent window, if we are a child window. Otherwise 0. + short BeginOrderWithinContext; // Begin() order within entire imgui context. This is mostly used for debugging submission order related issues. + short FocusOrder; // Order within WindowsFocusOrder[], altered when windows are focused. ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling) ImS8 AutoFitFramesX, AutoFitFramesY; ImS8 AutoFitChildAxises; From fdda8b8c12c740a141544192f29780ae997df47d Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 19 Apr 2021 14:03:21 +0200 Subject: [PATCH 3/5] ImDrawList: Fixed/improved thickness of thick strokes with sharp angles. (#4053, #3366, #2964, #2868, #2518, #2183) --- docs/CHANGELOG.txt | 4 ++++ imgui_draw.cpp | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6e497633..0bd685ab 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -55,6 +55,10 @@ Other Changes: - LabelText: Fixed clipping of multi-line value text when label is single-line. (#4004) - LabelText: Fixed vertical alignment of single-line value text when label is multi-line. (#4004) - Popups: Added 'OpenPopup(ImGuiID id)' overload to facilitate calling from nested stacks. (#3993, #331) [@zlash] +- ImDrawList: Fixed/improved thickness of thick strokes with sharp angles. (#4053, #3366, #2964, #2868, #2518, #2183) + Effectively introduced a regression in 1.67 (Jan 2019), and a fix in 1.70 (Apr 2019) but the fix wasn't actually on + par with original version. Now incorporating the correct fix + tweaked threshold to further mitigate the effect of + varying thickness, up to a certain degree. Shapes with very sharp angles and thick strokes will extend a little more. - ImDrawList: Fixed PathArcTo() regression from 1.82 preventing use of counter-clockwise angles. (#4030, #3491) [@thedmd] - Demo: Improved popups demo and comments. - Backends: SDL: Rework global mouse pos availability check listing supported platforms explicitly, diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 2b0fdb8f..2604ac1a 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -689,7 +689,8 @@ void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, c // On AddPolyline() and AddConvexPolyFilled() we intentionally avoid using ImVec2 and superfluous function calls to optimize debug/non-inlined builds. // Those macros expects l-values. #define IM_NORMALIZE2F_OVER_ZERO(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = 1.0f / ImSqrt(d2); VX *= inv_len; VY *= inv_len; } } while (0) -#define IM_FIXNORMAL2F(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 < 0.5f) d2 = 0.5f; float inv_lensq = 1.0f / d2; VX *= inv_lensq; VY *= inv_lensq; } while (0) +#define IM_FIXNORMAL2F_MAX_INVLEN2 500.0f +#define IM_FIXNORMAL2F(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 > 0.000001f) { float inv_len2 = 1.0f / d2; if (inv_len2 > IM_FIXNORMAL2F_MAX_INVLEN2) inv_len2 = IM_FIXNORMAL2F_MAX_INVLEN2; VX *= inv_len2; VY *= inv_len2; } } while (0) // TODO: Thickness anti-aliased lines cap are missing their AA fringe. // We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds. From 25fbff2156640cc79e9a79db00522019b4a0420f Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 19 Apr 2021 14:58:42 +0200 Subject: [PATCH 4/5] ImDrawList: Revert alteration of normal scaling threshold, for now prioritize preserving property of limiting extents. (#4053, #3366, #2964, #2868, #2518, #2183) Amend fdda8b8 --- docs/CHANGELOG.txt | 3 +-- imgui_draw.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0bd685ab..acd86b9d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -57,8 +57,7 @@ Other Changes: - Popups: Added 'OpenPopup(ImGuiID id)' overload to facilitate calling from nested stacks. (#3993, #331) [@zlash] - ImDrawList: Fixed/improved thickness of thick strokes with sharp angles. (#4053, #3366, #2964, #2868, #2518, #2183) Effectively introduced a regression in 1.67 (Jan 2019), and a fix in 1.70 (Apr 2019) but the fix wasn't actually on - par with original version. Now incorporating the correct fix + tweaked threshold to further mitigate the effect of - varying thickness, up to a certain degree. Shapes with very sharp angles and thick strokes will extend a little more. + par with original version. Now incorporating the correct revert. - ImDrawList: Fixed PathArcTo() regression from 1.82 preventing use of counter-clockwise angles. (#4030, #3491) [@thedmd] - Demo: Improved popups demo and comments. - Backends: SDL: Rework global mouse pos availability check listing supported platforms explicitly, diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 2604ac1a..8f851d26 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -689,7 +689,7 @@ void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, c // On AddPolyline() and AddConvexPolyFilled() we intentionally avoid using ImVec2 and superfluous function calls to optimize debug/non-inlined builds. // Those macros expects l-values. #define IM_NORMALIZE2F_OVER_ZERO(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = 1.0f / ImSqrt(d2); VX *= inv_len; VY *= inv_len; } } while (0) -#define IM_FIXNORMAL2F_MAX_INVLEN2 500.0f +#define IM_FIXNORMAL2F_MAX_INVLEN2 100.0f // 500.0f (see #4053, #3366) #define IM_FIXNORMAL2F(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 > 0.000001f) { float inv_len2 = 1.0f / d2; if (inv_len2 > IM_FIXNORMAL2F_MAX_INVLEN2) inv_len2 = IM_FIXNORMAL2F_MAX_INVLEN2; VX *= inv_len2; VY *= inv_len2; } } while (0) // TODO: Thickness anti-aliased lines cap are missing their AA fringe. From 6d5388448794f76cc92576a74ad3cb17da2fca84 Mon Sep 17 00:00:00 2001 From: rokups Date: Mon, 19 Apr 2021 13:47:31 +0300 Subject: [PATCH 5/5] Backends: OSX: Fix keys remaining stuck in pressed state when CMD-tabbing to a different application. (#3832) --- backends/imgui_impl_osx.mm | 43 +++++++++++++++++++++++++++++++------- docs/CHANGELOG.txt | 1 + 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 62bba023..354501e2 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -18,6 +18,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-04-19: Inputs: Added a fix for keys remaining stuck in pressed state when CMD-tabbing into different application. // 2021-01-27: Inputs: Added a fix for mouse position not being reported when mouse buttons other than left one are down. // 2020-10-28: Inputs: Added a fix for handling keypad-enter key. // 2020-05-25: Inputs: Added a fix for missing trackpad clicks when done with "soft tap". @@ -30,12 +31,15 @@ // 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. // 2018-07-07: Initial version. +@class ImFocusObserver; + // Data static CFAbsoluteTime g_Time = 0.0; static NSCursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {}; static bool g_MouseCursorHidden = false; static bool g_MouseJustPressed[ImGuiMouseButton_COUNT] = {}; static bool g_MouseDown[ImGuiMouseButton_COUNT] = {}; +static ImFocusObserver* g_FocusObserver = NULL; // Undocumented methods for creating cursors. @interface NSCursor() @@ -45,6 +49,31 @@ static bool g_MouseDown[ImGuiMouseButton_COUNT] = {}; + (id)_windowResizeEastWestCursor; @end +static void resetKeys() +{ + ImGuiIO& io = ImGui::GetIO(); + memset(io.KeysDown, 0, sizeof(io.KeysDown)); + io.KeyCtrl = io.KeyShift = io.KeyAlt = io.KeySuper = false; +} + +@interface ImFocusObserver : NSObject + +- (void)onApplicationBecomeInactive:(NSNotification*)aNotification; + +@end + +@implementation ImFocusObserver + +- (void)onApplicationBecomeInactive:(NSNotification*)aNotification +{ + // Unfocused applications do not receive input events, therefore we must manually + // release any pressed keys when application loses focus, otherwise they would remain + // stuck in a pressed state. https://github.com/ocornut/imgui/issues/3832 + resetKeys(); +} + +@end + // Functions bool ImGui_ImplOSX_Init() { @@ -123,11 +152,18 @@ bool ImGui_ImplOSX_Init() return s_clipboard.Data; }; + g_FocusObserver = [[ImFocusObserver alloc] init]; + [[NSNotificationCenter defaultCenter] addObserver:g_FocusObserver + selector:@selector(onApplicationBecomeInactive:) + name:NSApplicationDidResignActiveNotification + object:nil]; + return true; } void ImGui_ImplOSX_Shutdown() { + g_FocusObserver = NULL; } static void ImGui_ImplOSX_UpdateMouseCursorAndButtons() @@ -200,13 +236,6 @@ static int mapCharacterToKey(int c) return -1; } -static void resetKeys() -{ - ImGuiIO& io = ImGui::GetIO(); - for (int n = 0; n < IM_ARRAYSIZE(io.KeysDown); n++) - io.KeysDown[n] = false; -} - bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) { ImGuiIO& io = ImGui::GetIO(); diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index acd86b9d..56686f23 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -64,6 +64,7 @@ Other Changes: 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: OSX: Fix keys remaining stuck when CMD-tabbing to a different application. (#3832) [@rokups] - 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: DirectX10, DirectX11: fixed a crash when backing/restoring state if nothing is bound when