diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 0b7ef361..7a9b573e 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -3,8 +3,6 @@ name: static-analysis on: push: {} pull_request: {} - schedule: - - cron: '0 9 * * *' jobs: PVS-Studio: @@ -16,16 +14,17 @@ jobs: - name: Install Dependencies env: + # The Secret variable setup in GitHub must be in format: "name_or_email key", on a single line PVS_STUDIO_LICENSE: ${{ secrets.PVS_STUDIO_LICENSE }} run: | if [[ "$PVS_STUDIO_LICENSE" != "" ]]; then - echo "$PVS_STUDIO_LICENSE" > pvs-studio.lic wget -q https://files.viva64.com/etc/pubkey.txt sudo apt-key add pubkey.txt sudo wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list sudo apt-get update sudo apt-get install -y pvs-studio + pvs-studio-analyzer credentials -o pvs-studio.lic $PVS_STUDIO_LICENSE fi - name: PVS-Studio static analysis diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 050116f4..e0e4cc4c 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -23,6 +23,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2020-11-11: Vulkan: Added support for specifying which subpass to reference during VkPipeline creation. // 2020-09-07: Vulkan: Added VkPipeline parameter to ImGui_ImplVulkan_RenderDrawData (default to one passed to ImGui_ImplVulkan_Init). // 2020-05-04: Vulkan: Fixed crash if initial frame has no vertices. // 2020-04-26: Vulkan: Fixed edge case where render callbacks wouldn't be called if the ImDrawData didn't have vertices. @@ -94,6 +95,7 @@ static VkDescriptorSetLayout g_DescriptorSetLayout = VK_NULL_HANDLE; static VkPipelineLayout g_PipelineLayout = VK_NULL_HANDLE; static VkDescriptorSet g_DescriptorSet = VK_NULL_HANDLE; static VkPipeline g_Pipeline = VK_NULL_HANDLE; +static uint32_t g_Subpass = 0; static VkShaderModule g_ShaderModuleVert; static VkShaderModule g_ShaderModuleFrag; @@ -692,7 +694,7 @@ static void ImGui_ImplVulkan_CreatePipelineLayout(VkDevice device, const VkAlloc check_vk_result(err); } -static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationCallbacks* allocator, VkPipelineCache pipelineCache, VkRenderPass renderPass, VkSampleCountFlagBits MSAASamples, VkPipeline* pipeline) +static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationCallbacks* allocator, VkPipelineCache pipelineCache, VkRenderPass renderPass, VkSampleCountFlagBits MSAASamples, VkPipeline* pipeline, uint32_t subpass) { ImGui_ImplVulkan_CreateShaderModules(device, allocator); @@ -792,6 +794,7 @@ static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationC info.pDynamicState = &dynamic_state; info.layout = g_PipelineLayout; info.renderPass = renderPass; + info.subpass = subpass; VkResult err = vkCreateGraphicsPipelines(device, pipelineCache, 1, &info, allocator, pipeline); check_vk_result(err); } @@ -863,7 +866,7 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() check_vk_result(err); } - ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, v->PipelineCache, g_RenderPass, v->MSAASamples, &g_Pipeline); + ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, v->PipelineCache, g_RenderPass, v->MSAASamples, &g_Pipeline, g_Subpass); return true; } @@ -919,6 +922,8 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass rend g_VulkanInitInfo = *info; g_RenderPass = render_pass; + g_Subpass = info->Subpass; + ImGui_ImplVulkan_CreateDeviceObjects(); // Our render function expect RendererUserData to be storing the window render buffer we need (for the main viewport we won't use ->Window) @@ -1230,7 +1235,7 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V // We do not create a pipeline by default as this is also used by examples' main.cpp, // but secondary viewport in multi-viewport mode may want to create one with: - //ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->Pipeline); + //ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->Pipeline, g_Subpass); } // Create The Image Views diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h index 274535bc..c99e09a7 100644 --- a/backends/imgui_impl_vulkan.h +++ b/backends/imgui_impl_vulkan.h @@ -36,6 +36,7 @@ struct ImGui_ImplVulkan_InitInfo VkQueue Queue; VkPipelineCache PipelineCache; VkDescriptorPool DescriptorPool; + uint32_t Subpass; uint32_t MinImageCount; // >= 2 uint32_t ImageCount; // >= MinImageCount VkSampleCountFlagBits MSAASamples; // >= VK_SAMPLE_COUNT_1_BIT diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 2bae8038..40ac86f5 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -127,20 +127,36 @@ Other Changes: - Tab Bar: Made it possible to append to an existing tab bar by calling BeginTabBar()/EndTabBar() again. - Tab Bar: Fixed using more than 128 tabs in a tab bar (scrolling policy recommended). - Tab Bar: Do not display a tooltip if the name already fits over a given tab. (#3521) +- Tab Bar: Fixed minor/unlikely bug skipping over a button when scrolling left with arrows. - Drag and Drop: Fix losing drop source ActiveID (and often source tooltip) when opening a TreeNode() or CollapsingHeader() while dragging. (#1738) - Drag and Drop: Fix drag and drop to tie same-size drop targets by choosen the later one. Fixes dragging into a full-window-sized dockspace inside a zero-padded window. (#3519, #2717) [@Black-Cat] - Checkbox: Added CheckboxFlags() helper with int* type. +- InputText: Fixed updating cursor/selection position when a callback altered the buffer in a way + where the byte count is unchanged but the decoded character count changes. (#3587) [@gqw] +- Nav: Fixed IsItemFocused() from returning false when Nav highlight is hidden because mouse has moved. + It's essentially been always the case but it doesn't make much sense. Instead we will aim at exposing + feedback and control of keyboard/gamepad navigation highlight and mouse hover disable flag. (#787, #2048) - Metrics: Fixed mishandling of ImDrawCmd::VtxOffset in wireframe mesh renderer. -- Misc: Replaced UTF-8 decoder by branchless one by Christopher Wellons (30~40% faster). [@rokups] - Super minor fix handling incomplete UTF-8 contents: if input does not contain enough bytes, decoder +- Metrics: Rebranded as "Dear ImGui Metrics/Debugger" to clarify its purpose. +- Misc: Made the ItemFlags stack shared, so effectively the ButtonRepeat/AllowKeyboardFocus states + (and others exposed in internals such as PushItemFlag) are inherited by stacked Begin/End pairs, + vs previously a non-child stacked Begin() would reset those flags back to zero for the stacked window. +- Misc: Replaced UTF-8 decoder with one based on branchless one by Christopher Wellons. [@rokups] + Super minor fix handling incomplete UTF-8 contents: if input does not contain enough bytes, decoder returns IM_UNICODE_CODEPOINT_INVALID and consume remaining bytes (vs old decoded consumed only 1 byte). +- Misc: Fix format warnings when using gnu printf extensions in a setup that supports them (gcc/mingw). (#3592) +- Misc: Made EndFrame() assertion for key modifiers being unchanged during the frame (added in 1.76) more + lenient, allowing full mid-frame releases. This is to accommodate the use of mid-frame modal native + windows calls, which leads backends such as GLFW to send key clearing events on focus loss. (#3575) - Backends: OpenGL3: Use glGetString(GL_VERSION) query instead of glGetIntegerv(GL_MAJOR_VERSION, ...) when the later returns zero (e.g. Desktop GL 2.x). (#3530) [@xndcn] - Backends: OpenGL3: Backup and restore GL_PRIMITIVE_RESTART state. (#3544) [@Xipiryon] +- Backends: Vulkan: Added support for specifying which subpass to reference during VkPipeline creation. (@3579) [@bdero] - Backends: OSX: Fix keypad-enter key not working on MacOS. (#3554) [@rokups, @lfnoise] - Examples: Apple+Metal: Consolidated/simplified to get closer to other examples. (#3543) [@warrenm] +- Examples: Apple+Metal: Forward events down so OS key combination like Cmd+Q can work. (#3554) [@rokups] - Docs: Split examples/README.txt into docs/BACKENDS.md and docs/EXAMPLES.md improved them. - Docs: Consistently renamed all occurences of "binding" and "back-end" to "backend" in comments and docs. diff --git a/examples/example_apple_metal/main.mm b/examples/example_apple_metal/main.mm index 541dc5e1..015aee90 100644 --- a/examples/example_apple_metal/main.mm +++ b/examples/example_apple_metal/main.mm @@ -34,19 +34,19 @@ @implementation ViewController -- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil +- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; - + _device = MTLCreateSystemDefaultDevice(); _commandQueue = [_device newCommandQueue]; - if (!self.device) + if (!self.device) { NSLog(@"Metal is not supported"); abort(); } - + // Setup Dear ImGui context // FIXME: This example doesn't have proper cleanup... IMGUI_CHECKVERSION(); @@ -76,16 +76,16 @@ //io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f); //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); //IM_ASSERT(font != NULL); - + return self; } -- (MTKView *)mtkView +- (MTKView *)mtkView { return (MTKView *)self.view; } -- (void)loadView +- (void)loadView { self.view = [[MTKView alloc] initWithFrame:CGRectMake(0, 0, 1200, 720)]; } @@ -96,7 +96,7 @@ self.mtkView.device = self.device; self.mtkView.delegate = self; - + #if TARGET_OS_OSX // Add a tracking area in order to receive mouse events whenever the mouse is within the bounds of our view NSTrackingArea *trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect @@ -108,20 +108,17 @@ // If we want to receive key events, we either need to be in the responder chain of the key view, // or else we can install a local monitor. The consequence of this heavy-handed approach is that // we receive events for all controls, not just Dear ImGui widgets. If we had native controls in our - // window, we'd want to be much more careful than just ingesting the complete event stream, though we - // do make an effort to be good citizens by passing along events when Dear ImGui doesn't want to capture. + // window, we'd want to be much more careful than just ingesting the complete event stream. + // To match the behavior of other backends, we pass every event down to the OS. NSEventMask eventMask = NSEventMaskKeyDown | NSEventMaskKeyUp | NSEventMaskFlagsChanged | NSEventTypeScrollWheel; [NSEvent addLocalMonitorForEventsMatchingMask:eventMask handler:^NSEvent * _Nullable(NSEvent *event) { - BOOL wantsCapture = ImGui_ImplOSX_HandleEvent(event, self.view); - if (event.type == NSEventTypeKeyDown && wantsCapture) - return nil; - else - return event; + ImGui_ImplOSX_HandleEvent(event, self.view); + return event; }]; ImGui_ImplOSX_Init(); - + #endif } diff --git a/imconfig.h b/imconfig.h index 015540f3..736958b3 100644 --- a/imconfig.h +++ b/imconfig.h @@ -31,7 +31,7 @@ // It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp. //#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty. //#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. Not recommended. -//#define IMGUI_DISABLE_METRICS_WINDOW // Disable debug/metrics window: ShowMetricsWindow() will be empty. +//#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger window: ShowMetricsWindow() will be empty. //---- Don't implement some functions to reduce linkage requirements. //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. diff --git a/imgui.cpp b/imgui.cpp index b88cd693..7165637a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -884,7 +884,6 @@ static int FindWindowFocusIndex(ImGuiWindow* window); // Error Checking static void ErrorCheckNewFrameSanityChecks(); static void ErrorCheckEndFrameSanityChecks(); -static void ErrorCheckBeginEndCompareStacksSize(ImGuiWindow* window, bool write); // Misc static void UpdateSettings(); @@ -2389,7 +2388,7 @@ void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col) ImGuiColorMod backup; backup.Col = idx; backup.BackupValue = g.Style.Colors[idx]; - g.ColorModifiers.push_back(backup); + g.ColorStack.push_back(backup); g.Style.Colors[idx] = ColorConvertU32ToFloat4(col); } @@ -2399,7 +2398,7 @@ void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col) ImGuiColorMod backup; backup.Col = idx; backup.BackupValue = g.Style.Colors[idx]; - g.ColorModifiers.push_back(backup); + g.ColorStack.push_back(backup); g.Style.Colors[idx] = col; } @@ -2408,9 +2407,9 @@ void ImGui::PopStyleColor(int count) ImGuiContext& g = *GImGui; while (count > 0) { - ImGuiColorMod& backup = g.ColorModifiers.back(); + ImGuiColorMod& backup = g.ColorStack.back(); g.Style.Colors[backup.Col] = backup.BackupValue; - g.ColorModifiers.pop_back(); + g.ColorStack.pop_back(); count--; } } @@ -2464,7 +2463,7 @@ void ImGui::PushStyleVar(ImGuiStyleVar idx, float val) { ImGuiContext& g = *GImGui; float* pvar = (float*)var_info->GetVarPtr(&g.Style); - g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar)); + g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar)); *pvar = val; return; } @@ -2478,7 +2477,7 @@ void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) { ImGuiContext& g = *GImGui; ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style); - g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar)); + g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar)); *pvar = val; return; } @@ -2491,12 +2490,12 @@ void ImGui::PopStyleVar(int count) while (count > 0) { // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it. - ImGuiStyleMod& backup = g.StyleModifiers.back(); + ImGuiStyleMod& backup = g.StyleVarStack.back(); const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx); void* data = info->GetVarPtr(&g.Style); if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; } else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; } - g.StyleModifiers.pop_back(); + g.StyleVarStack.pop_back(); count--; } } @@ -2813,83 +2812,31 @@ void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFl //----------------------------------------------------------------------------- // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods -ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) - : DrawListInst(&context->DrawListSharedData) +ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) : DrawListInst(NULL) { + memset(this, 0, sizeof(*this)); Name = ImStrdup(name); + NameBufLen = (int)strlen(name) + 1; ID = ImHashStr(name); IDStack.push_back(ID); - Flags = FlagsPreviousFrame = ImGuiWindowFlags_None; - Viewport = NULL; - ViewportId = 0; ViewportAllowPlatformMonitorExtend = -1; ViewportPos = ImVec2(FLT_MAX, FLT_MAX); - Pos = ImVec2(0.0f, 0.0f); - Size = SizeFull = ImVec2(0.0f, 0.0f); - ContentSize = ContentSizeExplicit = ImVec2(0.0f, 0.0f); - WindowPadding = ImVec2(0.0f, 0.0f); - WindowRounding = 0.0f; - WindowBorderSize = 0.0f; - NameBufLen = (int)strlen(name) + 1; MoveId = GetID("#MOVE"); - ChildId = 0; - Scroll = ImVec2(0.0f, 0.0f); ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f); - ScrollbarSizes = ImVec2(0.0f, 0.0f); - ScrollbarX = ScrollbarY = false; - ViewportOwned = false; - Active = WasActive = false; - WriteAccessed = false; - Collapsed = false; - WantCollapseToggle = false; - SkipItems = false; - Appearing = false; - Hidden = false; - IsFallbackWindow = false; - HasCloseButton = false; - ResizeBorderHeld = -1; - BeginCount = 0; - BeginOrderWithinParent = -1; - BeginOrderWithinContext = -1; - PopupId = 0; AutoFitFramesX = AutoFitFramesY = -1; - AutoFitChildAxises = 0x00; - AutoFitOnlyGrows = false; AutoPosLastDirection = ImGuiDir_None; - HiddenFramesCanSkipItems = HiddenFramesCannotSkipItems = 0; SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = SetWindowDockAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX); - - InnerRect = ImRect(0.0f, 0.0f, 0.0f, 0.0f); // Clear so the InnerRect.GetSize() code in Begin() doesn't lead to overflow even if the result isn't used. - LastFrameActive = -1; LastFrameJustFocused = -1; LastTimeActive = -1.0f; - ItemWidthDefault = 0.0f; FontWindowScale = FontDpiScale = 1.0f; SettingsOffset = -1; - - DrawList = &DrawListInst; - DrawList->_OwnerName = Name; - ParentWindow = NULL; - RootWindow = NULL; - RootWindowDockStop = NULL; - RootWindowForTitleBarHighlight = NULL; - RootWindowForNav = NULL; - - NavLastIds[0] = NavLastIds[1] = 0; - NavRectRel[0] = NavRectRel[1] = ImRect(); - NavLastChildNavWindow = NULL; - - MemoryCompacted = false; - MemoryDrawListIdxCapacity = MemoryDrawListVtxCapacity = 0; - - DockNode = DockNodeAsHost = NULL; - DockId = 0; - DockTabItemStatusFlags = ImGuiItemStatusFlags_None; DockOrder = -1; - DockIsActive = DockTabIsVisible = DockTabWantClose = false; + DrawList = &DrawListInst; + DrawList->_Data = &context->DrawListSharedData; + DrawList->_OwnerName = Name; } ImGuiWindow::~ImGuiWindow() @@ -2987,11 +2934,16 @@ static void SetCurrentWindow(ImGuiWindow* window) g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize(); } +void ImGui::GcCompactTransientMiscBuffers() +{ + ImGuiContext& g = *GImGui; + g.ItemFlagsStack.clear(); + g.GroupStack.clear(); +} + // Free up/compact internal window buffers, we can use this when a window becomes unused. -// This is currently unused by the library, but you may call this yourself for easy GC. // Not freed: -// - ImGuiWindow, ImGuiWindowSettings, Name -// - StateStorage, ColumnsStorage (may hold useful data) +// - ImGuiWindow, ImGuiWindowSettings, Name, StateStorage, ColumnsStorage (may hold useful data) // This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost. void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window) { @@ -3001,10 +2953,8 @@ void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window) window->IDStack.clear(); window->DrawList->_ClearFreeMemory(); window->DC.ChildWindows.clear(); - window->DC.ItemFlagsStack.clear(); window->DC.ItemWidthStack.clear(); window->DC.TextWrapPosStack.clear(); - window->DC.GroupStack.clear(); } void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window) @@ -4066,7 +4016,7 @@ void ImGui::NewFrame() // Mark all windows as not visible and compact unused memory. IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size); - const float memory_compact_start_time = (g.IO.ConfigMemoryCompactTimer >= 0.0f) ? (float)g.Time - g.IO.ConfigMemoryCompactTimer : FLT_MAX; + 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++) { ImGuiWindow* window = g.Windows[i]; @@ -4079,6 +4029,9 @@ void ImGui::NewFrame() if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time) GcCompactTransientWindowBuffers(window); } + if (g.GcCompactAll) + GcCompactTransientMiscBuffers(); + g.GcCompactAll = false; // Closing the focused window restore focus to the first active root window in descending z-order if (g.NavWindow && !g.NavWindow->WasActive) @@ -4088,6 +4041,9 @@ void ImGui::NewFrame() // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear. g.CurrentWindowStack.resize(0); g.BeginPopupStack.resize(0); + g.ItemFlagsStack.resize(0); + g.ItemFlagsStack.push_back(ImGuiItemFlags_Default_); + g.GroupStack.resize(0); ClosePopupsOverWindow(g.NavWindow, false); // Docking @@ -4229,8 +4185,8 @@ void ImGui::Shutdown(ImGuiContext* context) g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL; g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL; g.MovingWindow = NULL; - g.ColorModifiers.clear(); - g.StyleModifiers.clear(); + g.ColorStack.clear(); + g.StyleVarStack.clear(); g.FontStack.clear(); g.OpenPopupStack.clear(); g.BeginPopupStack.clear(); @@ -4296,7 +4252,7 @@ static void AddWindowToSortBuffer(ImVector* out_sorted_windows, Im static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list) { // Remove trailing command if unused. - // Technically we could return directly instead of popping, but this make things looks neat in Metrics window as well. + // Technically we could return directly instead of popping, but this make things looks neat in Metrics/Debugger window as well. draw_list->_PopUnusedDrawCmd(); if (draw_list->CmdBuffer.Size == 0) return; @@ -4311,7 +4267,7 @@ static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* d // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window) // If this assert triggers because you are drawing lots of stuff manually: // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds. - // Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics window to inspect draw list contents. + // Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents. // - If you want large meshes with more than 64K vertices, you can either: // (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'. // Most example backends already support this from 1.71. Pre-1.71 backends won't. @@ -4989,12 +4945,13 @@ bool ImGui::IsItemDeactivatedAfterEdit() return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore)); } +// == GetItemID() == GetFocusID() bool ImGui::IsItemFocused() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - if (g.NavId == 0 || g.NavDisableHighlight || g.NavId != window->DC.LastItemId) + if (g.NavId != window->DC.LastItemId || g.NavId == 0) return false; // Special handling for the dummy item after Begin() which represent the title bar or tab. @@ -5971,8 +5928,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Add to stack // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow() g.CurrentWindowStack.push_back(window); + g.CurrentWindow = window; + window->DC.StackSizesOnBegin.SetToCurrentState(); g.CurrentWindow = NULL; - ErrorCheckBeginEndCompareStacksSize(window, true); + if (flags & ImGuiWindowFlags_Popup) { ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; @@ -6569,7 +6528,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.NavLayerCurrent = ImGuiNavLayer_Main; window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext; window->DC.NavLayerActiveMaskNext = 0x00; - window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : 0; // -V595 window->DC.NavHideHighlightOneFrame = false; window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f); @@ -6586,13 +6544,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.ItemWidth = window->ItemWidthDefault; window->DC.TextWrapPos = -1.0f; // disabled - window->DC.ItemFlagsStack.resize(0); window->DC.ItemWidthStack.resize(0); window->DC.TextWrapPosStack.resize(0); - window->DC.GroupStack.resize(0); - window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_; - if (parent_window) - window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags); if (window->AutoFitFramesX > 0) window->AutoFitFramesX--; @@ -6668,13 +6621,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) SetCurrentWindow(window); } + // Pull/inherit current state + window->DC.ItemFlags = g.ItemFlagsStack.back(); // Inherit from shared stack + window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : 0; // Inherit from parent only // -V595 + if (!(flags & ImGuiWindowFlags_DockNodeHost)) PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true); // Clear 'accessed' flag last thing (After PushClipRect which will set the flag. We want the flag to stay false when the default "Debug" window is unused) - if (first_begin_of_the_frame) - window->WriteAccessed = false; - + window->WriteAccessed = false; window->BeginCount++; g.NextWindowData.ClearFlags(); @@ -6763,7 +6718,7 @@ void ImGui::End() g.CurrentWindowStack.pop_back(); if (window->Flags & ImGuiWindowFlags_Popup) g.BeginPopupStack.pop_back(); - ErrorCheckBeginEndCompareStacksSize(window, false); + window->DC.StackSizesOnBegin.CompareWithCurrentState(); SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back()); if (g.CurrentWindow) SetCurrentViewport(g.CurrentWindow, g.CurrentWindow->Viewport); @@ -6929,19 +6884,25 @@ void ImGui::PopFont() void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled) { - ImGuiWindow* window = GetCurrentWindow(); + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiItemFlags item_flags = window->DC.ItemFlags; + IM_ASSERT(item_flags == g.ItemFlagsStack.back()); if (enabled) - window->DC.ItemFlags |= option; + item_flags |= option; else - window->DC.ItemFlags &= ~option; - window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags); + item_flags &= ~option; + window->DC.ItemFlags = item_flags; + g.ItemFlagsStack.push_back(item_flags); } void ImGui::PopItemFlag() { - ImGuiWindow* window = GetCurrentWindow(); - window->DC.ItemFlagsStack.pop_back(); - window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back(); + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + IM_ASSERT(g.ItemFlagsStack.Size > 1); // Too many calls to PopItemFlag() - we always leave a 0 at the bottom of the stack. + g.ItemFlagsStack.pop_back(); + window->DC.ItemFlags = g.ItemFlagsStack.back(); } // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system. @@ -7371,12 +7332,11 @@ void ImGui::ActivateItem(ImGuiID id) g.NavNextActivateId = id; } -// Note: this is storing in same stack as IDStack, so Push/Pop mismatch will be reported there. void ImGui::PushFocusScope(ImGuiID id) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - window->IDStack.push_back(window->DC.NavFocusScopeIdCurrent); + g.FocusScopeStack.push_back(window->DC.NavFocusScopeIdCurrent); window->DC.NavFocusScopeIdCurrent = id; } @@ -7384,8 +7344,9 @@ void ImGui::PopFocusScope() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - window->DC.NavFocusScopeIdCurrent = window->IDStack.back(); - window->IDStack.pop_back(); + IM_ASSERT(g.FocusScopeStack.Size > 0); // Too many PopFocusScope() ? + window->DC.NavFocusScopeIdCurrent = g.FocusScopeStack.back(); + g.FocusScopeStack.pop_back(); } void ImGui::SetKeyboardFocusHere(int offset) @@ -7484,6 +7445,7 @@ ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed) void ImGui::PopID() { ImGuiWindow* window = GImGui->CurrentWindow; + IM_ASSERT(window->IDStack.Size > 1); // Too many PopID(), or could be popping in a wrong/different window? window->IDStack.pop_back(); } @@ -7622,9 +7584,13 @@ static void ImGui::ErrorCheckEndFrameSanityChecks() // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame() // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame(). - const ImGuiKeyModFlags expected_key_mod_flags = GetMergedKeyModFlags(); - IM_ASSERT(g.IO.KeyMods == expected_key_mod_flags && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods"); - IM_UNUSED(expected_key_mod_flags); + // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will + // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs. + // We silently accommodate for this case by ignoring/ the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0), + // while still correctly asserting on mid-frame key press events. + const ImGuiKeyModFlags key_mod_flags = GetMergedKeyModFlags(); + IM_ASSERT((key_mod_flags == 0 || g.IO.KeyMods == key_mod_flags) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods"); + IM_UNUSED(key_mod_flags); // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API). @@ -7641,28 +7607,42 @@ static void ImGui::ErrorCheckEndFrameSanityChecks() IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?"); } } + + IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!"); } -// Save and compare stack sizes on Begin()/End() to detect usage errors -// Begin() calls this with write=true -// End() calls this with write=false -static void ImGui::ErrorCheckBeginEndCompareStacksSize(ImGuiWindow* window, bool write) +// Save current stack sizes for later compare +void ImGuiStackSizes::SetToCurrentState() { ImGuiContext& g = *GImGui; - short* p = &window->DC.StackSizesBackup[0]; + ImGuiWindow* window = g.CurrentWindow; + SizeOfIDStack = (short)window->IDStack.Size; + SizeOfColorStack = (short)g.ColorStack.Size; + SizeOfStyleVarStack = (short)g.StyleVarStack.Size; + SizeOfFontStack = (short)g.FontStack.Size; + SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size; + SizeOfGroupStack = (short)g.GroupStack.Size; + SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size; +} + +// Compare to detect usage errors +void ImGuiStackSizes::CompareWithCurrentState() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; // Window stacks - // NOT checking: DC.ItemWidth, DC.AllowKeyboardFocus, DC.ButtonRepeat, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin) - { int n = window->IDStack.Size; if (write) *p = (short)n; else IM_ASSERT(*p == n && "PushID/PopID or TreeNode/TreePop Mismatch!"); p++; } // Too few or too many PopID()/TreePop() - { int n = window->DC.GroupStack.Size; if (write) *p = (short)n; else IM_ASSERT(*p == n && "BeginGroup/EndGroup Mismatch!"); p++; } // Too few or too many EndGroup() + // NOT checking: DC.ItemWidth, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin) + IM_ASSERT(SizeOfIDStack == window->IDStack.Size && "PushID/PopID or TreeNode/TreePop Mismatch!"); // Global stacks // For color, style and font stacks there is an incentive to use Push/Begin/Pop/.../End patterns, so we relax our checks a little to allow them. - { int n = g.BeginPopupStack.Size; if (write) *p = (short)n; else IM_ASSERT(*p == n && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch!"); p++; }// Too few or too many EndMenu()/EndPopup() - { int n = g.ColorModifiers.Size; if (write) *p = (short)n; else IM_ASSERT(*p >= n && "PushStyleColor/PopStyleColor Mismatch!"); p++; } // Too few or too many PopStyleColor() - { int n = g.StyleModifiers.Size; if (write) *p = (short)n; else IM_ASSERT(*p >= n && "PushStyleVar/PopStyleVar Mismatch!"); p++; } // Too few or too many PopStyleVar() - { int n = g.FontStack.Size; if (write) *p = (short)n; else IM_ASSERT(*p >= n && "PushFont/PopFont Mismatch!"); p++; } // Too few or too many PopFont() - IM_ASSERT(p == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup)); + IM_ASSERT(SizeOfGroupStack == g.GroupStack.Size && "BeginGroup/EndGroup Mismatch!"); + IM_ASSERT(SizeOfBeginPopupStack == g.BeginPopupStack.Size && "BeginPopup/EndPopup or BeginMenu/EndMenu Mismatch!"); + IM_ASSERT(SizeOfColorStack >= g.ColorStack.Size && "PushStyleColor/PopStyleColor Mismatch!"); + IM_ASSERT(SizeOfStyleVarStack >= g.StyleVarStack.Size && "PushStyleVar/PopStyleVar Mismatch!"); + IM_ASSERT(SizeOfFontStack >= g.FontStack.Size && "PushFont/PopFont Mismatch!"); + IM_ASSERT(SizeOfFocusScopeStack == g.FocusScopeStack.Size && "PushFocusScope/PopFocusScope Mismatch!"); } @@ -8065,8 +8045,9 @@ void ImGui::BeginGroup() ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1); - ImGuiGroupData& group_data = window->DC.GroupStack.back(); + g.GroupStack.resize(g.GroupStack.Size + 1); + ImGuiGroupData& group_data = g.GroupStack.back(); + group_data.WindowID = window->ID; group_data.BackupCursorPos = window->DC.CursorPos; group_data.BackupCursorMaxPos = window->DC.CursorMaxPos; group_data.BackupIndent = window->DC.Indent; @@ -8089,9 +8070,10 @@ void ImGui::EndGroup() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT(window->DC.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls + IM_ASSERT(g.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls - ImGuiGroupData& group_data = window->DC.GroupStack.back(); + ImGuiGroupData& group_data = g.GroupStack.back(); + IM_ASSERT(group_data.WindowID == window->ID); // EndGroup() in wrong window? ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos)); @@ -8106,7 +8088,7 @@ void ImGui::EndGroup() if (!group_data.EmitItem) { - window->DC.GroupStack.pop_back(); + g.GroupStack.pop_back(); return; } @@ -8135,7 +8117,7 @@ void ImGui::EndGroup() if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame) window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Deactivated; - window->DC.GroupStack.pop_back(); + g.GroupStack.pop_back(); //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug] } @@ -15543,7 +15525,7 @@ static void MetricsHelpMarker(const char* desc) void ImGui::ShowMetricsWindow(bool* p_open) { - if (!Begin("Dear ImGui Metrics", p_open)) + if (!Begin("Dear ImGui Metrics/Debugger", p_open)) { End(); return; @@ -15559,6 +15541,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3); Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows); Text("%d active allocations", io.MetricsActiveAllocations); + //SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; } + Separator(); // Debugging enums @@ -15569,7 +15553,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (cfg->ShowWindowsRectsType < 0) cfg->ShowWindowsRectsType = WRT_WorkRect; if (cfg->ShowTablesRectsType < 0) - cfg->ShowWindowsRectsType = TRT_WorkRect; + cfg->ShowTablesRectsType = TRT_WorkRect; struct Funcs { @@ -15597,7 +15581,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash."); Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder); - ImGui::Checkbox("Show windows rectangles", &cfg->ShowWindowsRects); + Checkbox("Show windows rectangles", &cfg->ShowWindowsRects); SameLine(); SetNextItemWidth(GetFontSize() * 12); cfg->ShowWindowsRects |= Combo("##show_windows_rect_type", &cfg->ShowWindowsRectsType, wrt_rects_names, WRT_Count, WRT_Count); @@ -15861,7 +15845,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) } #endif // #ifdef IMGUI_HAS_DOCK - ImGui::End(); + End(); } // [DEBUG] Display contents of Columns diff --git a/imgui.h b/imgui.h index af86e94c..b40a2c14 100644 --- a/imgui.h +++ b/imgui.h @@ -81,13 +81,6 @@ Index of this file: #include #define IM_ASSERT(_EXPR) assert(_EXPR) // You can override the default assert handler by editing imconfig.h #endif -#if !defined(IMGUI_USE_STB_SPRINTF) && (defined(__clang__) || defined(__GNUC__)) -#define IM_FMTARGS(FMT) __attribute__((format(printf, FMT, FMT+1))) // To apply printf-style warnings to our functions. -#define IM_FMTLIST(FMT) __attribute__((format(printf, FMT, 0))) -#else -#define IM_FMTARGS(FMT) -#define IM_FMTLIST(FMT) -#endif #define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR) / sizeof(*(_ARR)))) // Size of a static C-style array. Don't use on pointers! #define IM_UNUSED(_VAR) ((void)(_VAR)) // Used to silence "unused variable warnings". Often useful as asserts may be stripped out from final builds. #if (__cplusplus >= 201100) @@ -95,6 +88,16 @@ Index of this file: #else #define IM_OFFSETOF(_TYPE,_MEMBER) ((size_t)&(((_TYPE*)0)->_MEMBER)) // Offset of _MEMBER within _TYPE. Old style macro. #endif +#if !defined(IMGUI_USE_STB_SPRINTF) && defined(__clang__) +#define IM_FMTARGS(FMT) __attribute__((format(printf, FMT, FMT+1))) // Apply printf-style warnings to our formatting functions. +#define IM_FMTLIST(FMT) __attribute__((format(printf, FMT, 0))) +#elif !defined(IMGUI_USE_STB_SPRINTF) && defined(__GNUC__) && defined(__MINGW32__) +#define IM_FMTARGS(FMT) __attribute__((format(gnu_printf, FMT, FMT+1))) // Apply printf-style warnings to our formatting functions. +#define IM_FMTLIST(FMT) __attribute__((format(gnu_printf, FMT, 0))) +#else +#define IM_FMTARGS(FMT) +#define IM_FMTLIST(FMT) +#endif // Warnings #if defined(__clang__) @@ -266,7 +269,7 @@ namespace ImGui // Demo, Debug, Information IMGUI_API void ShowDemoWindow(bool* p_open = NULL); // create Demo window (previously called ShowTestWindow). demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application! IMGUI_API void ShowAboutWindow(bool* p_open = NULL); // create About window. display Dear ImGui version, credits and build/system information. - IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // create Debug/Metrics window. display Dear ImGui internals: draw commands (with individual draw calls and vertices), window list, basic internal state, etc. + IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // create Metrics/Debugger window. display Dear ImGui internals: windows, draw commands, various internal state, etc. IMGUI_API void ShowStyleEditor(ImGuiStyle* ref = NULL); // add style editor block (not a window). you can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it uses the default style) IMGUI_API bool ShowStyleSelector(const char* label); // add style selector block (not a window), essentially a combo listing the default styles. IMGUI_API void ShowFontSelector(const char* label); // add font selector block (not a window), essentially a combo listing the loaded fonts. @@ -297,7 +300,10 @@ namespace ImGui // - Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window. Child windows can embed their own child. // - For each independent axis of 'size': ==0.0f: use remaining host window size / >0.0f: fixed size / <0.0f: use remaining window size minus abs(size) / Each axis can use a different mode, e.g. ImVec2(0,400). // - BeginChild() returns false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting anything to the window. - // Always call a matching EndChild() for each BeginChild() call, regardless of its return value [as with Begin: this is due to legacy reason and inconsistent with most BeginXXX functions apart from the regular Begin() which behaves like BeginChild().] + // Always call a matching EndChild() for each BeginChild() call, regardless of its return value. + // [Important: due to legacy reason, this is inconsistent with most other functions such as BeginMenu/EndMenu, + // BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding BeginXXX function + // returned true. Begin and BeginChild are the only odd ones out. Will be fixed in a future update.] IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2(0, 0), bool border = false, ImGuiWindowFlags flags = 0); IMGUI_API bool BeginChild(ImGuiID id, const ImVec2& size = ImVec2(0, 0), bool border = false, ImGuiWindowFlags flags = 0); IMGUI_API void EndChild(); @@ -364,6 +370,10 @@ namespace ImGui IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val); IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val); IMGUI_API void PopStyleVar(int count = 1); + IMGUI_API void PushAllowKeyboardFocus(bool allow_keyboard_focus); // allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets + IMGUI_API void PopAllowKeyboardFocus(); + IMGUI_API void PushButtonRepeat(bool repeat); // in 'repeat' mode, Button*() functions return repeated true in a typematic manner (using io.KeyRepeatDelay/io.KeyRepeatRate setting). Note that you can call IsItemActive() after any Button() to tell if the button is held in the current frame. + IMGUI_API void PopButtonRepeat(); IMGUI_API const ImVec4& GetStyleColorVec4(ImGuiCol idx); // retrieve style color as stored in ImGuiStyle structure. use to feed back into PushStyleColor(), otherwise use GetColorU32() to get style color with style alpha baked in. IMGUI_API ImFont* GetFont(); // get current font IMGUI_API float GetFontSize(); // get current font size (= height in pixels) of current font with current scale applied @@ -379,10 +389,6 @@ namespace ImGui IMGUI_API float CalcItemWidth(); // width of item given pushed settings and current cursor position. NOT necessarily the width of last item unlike most 'Item' functions. IMGUI_API void PushTextWrapPos(float wrap_local_pos_x = 0.0f); // push word-wrapping position for Text*() commands. < 0.0f: no wrapping; 0.0f: wrap to end of window (or column); > 0.0f: wrap at 'wrap_pos_x' position in window local space IMGUI_API void PopTextWrapPos(); - IMGUI_API void PushAllowKeyboardFocus(bool allow_keyboard_focus); // allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets - IMGUI_API void PopAllowKeyboardFocus(); - IMGUI_API void PushButtonRepeat(bool repeat); // in 'repeat' mode, Button*() functions return repeated true in a typematic manner (using io.KeyRepeatDelay/io.KeyRepeatRate setting). Note that you can call IsItemActive() after any Button() to tell if the button is held in the current frame. - IMGUI_API void PopButtonRepeat(); // Cursor / Layout // - By "cursor" we mean the current output position. @@ -396,8 +402,8 @@ namespace ImGui IMGUI_API void NewLine(); // undo a SameLine() or force a new line when in an horizontal-layout context. IMGUI_API void Spacing(); // add vertical spacing. IMGUI_API void Dummy(const ImVec2& size); // add a dummy item of given size. unlike InvisibleButton(), Dummy() won't take the mouse click or be navigable into. - IMGUI_API void Indent(float indent_w = 0.0f); // move content position toward the right, by style.IndentSpacing or indent_w if != 0 - IMGUI_API void Unindent(float indent_w = 0.0f); // move content position back to the left, by style.IndentSpacing or indent_w if != 0 + IMGUI_API void Indent(float indent_w = 0.0f); // move content position toward the right, by indent_w, or style.IndentSpacing if indent_w <= 0 + IMGUI_API void Unindent(float indent_w = 0.0f); // move content position back to the left, by indent_w, or style.IndentSpacing if indent_w <= 0 IMGUI_API void BeginGroup(); // lock horizontal starting position IMGUI_API void EndGroup(); // unlock horizontal starting position + capture the whole group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) IMGUI_API ImVec2 GetCursorPos(); // cursor position in window coordinates (relative to window position) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index f331676a..b1c900bb 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -359,7 +359,7 @@ void ImGui::ShowDemoWindow(bool* p_open) } if (ImGui::BeginMenu("Tools")) { - ImGui::MenuItem("Metrics", NULL, &show_app_metrics); + ImGui::MenuItem("Metrics/Debugger", NULL, &show_app_metrics); ImGui::MenuItem("Style Editor", NULL, &show_app_style_editor); ImGui::MenuItem("About Dear ImGui", NULL, &show_app_about); ImGui::EndMenu(); @@ -376,7 +376,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::BulletText("Sections below are demonstrating many aspects of the library."); ImGui::BulletText("The \"Examples\" menu above leads to more demo contents."); ImGui::BulletText("The \"Tools\" menu above gives access to: About Box, Style Editor,\n" - "and Metrics (general purpose Dear ImGui debugging tool)."); + "and Metrics/Debugger (general purpose Dear ImGui debugging tool)."); ImGui::Separator(); ImGui::Text("PROGRAMMER GUIDE:"); @@ -399,8 +399,9 @@ void ImGui::ShowDemoWindow(bool* p_open) if (ImGui::TreeNode("Configuration##2")) { ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", &io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard); + ImGui::SameLine(); HelpMarker("Enable keyboard controls."); ImGui::CheckboxFlags("io.ConfigFlags: NavEnableGamepad", &io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad); - ImGui::SameLine(); HelpMarker("Required backend to feed in gamepad inputs in io.NavInputs[] and set io.BackendFlags |= ImGuiBackendFlags_HasGamepad.\n\nRead instructions in imgui.cpp for details."); + ImGui::SameLine(); HelpMarker("Enable gamepad controls. Require backend to set io.BackendFlags |= ImGuiBackendFlags_HasGamepad.\n\nRead instructions in imgui.cpp for details."); ImGui::CheckboxFlags("io.ConfigFlags: NavEnableSetMousePos", &io.ConfigFlags, ImGuiConfigFlags_NavEnableSetMousePos); ImGui::SameLine(); HelpMarker("Instruct navigation to move the mouse cursor. See comment for ImGuiConfigFlags_NavEnableSetMousePos."); ImGui::CheckboxFlags("io.ConfigFlags: NoMouse", &io.ConfigFlags, ImGuiConfigFlags_NoMouse); @@ -3630,7 +3631,7 @@ static void ShowDemoWindowMisc() char label[32]; sprintf(label, "Mouse cursor %d: %s", i, mouse_cursors_names[i]); ImGui::Bullet(); ImGui::Selectable(label, false); - if (ImGui::IsItemHovered() || ImGui::IsItemFocused()) + if (ImGui::IsItemHovered()) ImGui::SetMouseCursor(i); } ImGui::TreePop(); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 2dfe0121..1db9586d 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -60,6 +60,9 @@ Index of this file: #if __has_warning("-Wunknown-warning-option") #pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great! #endif +#if __has_warning("-Walloca") +#pragma clang diagnostic ignored "-Walloca" // warning: use of function '__builtin_alloca' is discouraged +#endif #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' #pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse. #pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok. @@ -1423,6 +1426,7 @@ void ImDrawListSplitter::ClearFreeMemory() void ImDrawListSplitter::Split(ImDrawList* draw_list, int channels_count) { + IM_UNUSED(draw_list); IM_ASSERT(_Current == 0 && _Count <= 1 && "Nested channel splitting is not supported. Please use separate instances of ImDrawListSplitter."); int old_channels_count = _Channels.Size; if (old_channels_count < channels_count) @@ -1447,12 +1451,6 @@ void ImDrawListSplitter::Split(ImDrawList* draw_list, int channels_count) _Channels[i]._CmdBuffer.resize(0); _Channels[i]._IdxBuffer.resize(0); } - if (_Channels[i]._CmdBuffer.Size == 0) - { - ImDrawCmd draw_cmd; - ImDrawCmd_HeaderCopy(&draw_cmd, &draw_list->_CmdHeader); // Copy ClipRect, TextureId, VtxOffset - _Channels[i]._CmdBuffer.push_back(draw_cmd); - } } } @@ -1542,8 +1540,10 @@ void ImDrawListSplitter::SetCurrentChannel(ImDrawList* draw_list, int idx) draw_list->_IdxWritePtr = draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size; // If current command is used with different settings we need to add a new command - ImDrawCmd* curr_cmd = &draw_list->CmdBuffer.Data[draw_list->CmdBuffer.Size - 1]; - if (curr_cmd->ElemCount == 0) + ImDrawCmd* curr_cmd = (draw_list->CmdBuffer.Size == 0) ? NULL : &draw_list->CmdBuffer.Data[draw_list->CmdBuffer.Size - 1]; + if (curr_cmd == NULL) + draw_list->AddDrawCmd(); + else if (curr_cmd->ElemCount == 0) ImDrawCmd_HeaderCopy(curr_cmd, &draw_list->_CmdHeader); // Copy ClipRect, TextureId, VtxOffset else if (ImDrawCmd_HeaderCompare(curr_cmd, &draw_list->_CmdHeader) != 0) draw_list->AddDrawCmd(); diff --git a/imgui_internal.h b/imgui_internal.h index 64ecedfc..f9ff8098 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -23,6 +23,7 @@ Index of this file: // [SECTION] Docking support // [SECTION] Viewport support // [SECTION] Settings support +// [SECTION] Metrics, Debug // [SECTION] Generic context hooks // [SECTION] ImGuiContext (main imgui context) // [SECTION] ImGuiWindowTempData, ImGuiWindow @@ -110,6 +111,7 @@ struct ImGuiNextWindowData; // Storage for SetNextWindow** functions struct ImGuiNextItemData; // Storage for SetNextItem** functions struct ImGuiPopupData; // Storage for current popup stack struct ImGuiSettingsHandler; // Storage for one type registered in the .ini file +struct ImGuiStackSizes; // Storage of stack sizes for debugging/asserting struct ImGuiStyleMod; // Stacked style modifier, backup of modified data so we can restore it struct ImGuiTabBar; // Storage for a tab bar struct ImGuiTabItem; // Storage for a tab item (within a tab bar) @@ -465,15 +467,15 @@ struct IMGUI_API ImRect }; // Helper: ImBitArray -inline bool ImBitArrayTestBit(const ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); return (arr[n >> 5] & mask) != 0; } -inline void ImBitArrayClearBit(ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); arr[n >> 5] &= ~mask; } -inline void ImBitArraySetBit(ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); arr[n >> 5] |= mask; } -inline void ImBitArraySetBitRange(ImU32* arr, int n, int n2) +inline bool ImBitArrayTestBit(const ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); return (arr[n >> 5] & mask) != 0; } +inline void ImBitArrayClearBit(ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); arr[n >> 5] &= ~mask; } +inline void ImBitArraySetBit(ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); arr[n >> 5] |= mask; } +inline void ImBitArraySetBitRange(ImU32* arr, int n, int n2) { while (n <= n2) { int a_mod = (n & 31); - int b_mod = ((n2 >= n + 31) ? 31 : (n2 & 31)) + 1; + int b_mod = (n2 > (n | 31) ? 31 : (n2 & 31)) + 1; ImU32 mask = (ImU32)(((ImU64)1 << b_mod) - 1) & ~(ImU32)(((ImU64)1 << a_mod) - 1); arr[n >> 5] |= mask; n = (n + 32) & ~31; @@ -838,6 +840,7 @@ struct ImGuiStyleMod // Stacked storage data for BeginGroup()/EndGroup() struct ImGuiGroupData { + ImGuiID WindowID; ImVec2 BackupCursorPos; ImVec2 BackupCursorMaxPos; ImVec1 BackupIndent; @@ -1250,6 +1253,10 @@ struct ImGuiSettingsHandler ImGuiSettingsHandler() { memset(this, 0, sizeof(*this)); } }; +//----------------------------------------------------------------------------- +// [SECTION] Metrics, Debug +//----------------------------------------------------------------------------- + struct ImGuiMetricsConfig { bool ShowWindowsRects; @@ -1274,6 +1281,21 @@ struct ImGuiMetricsConfig } }; +struct IMGUI_API ImGuiStackSizes +{ + short SizeOfIDStack; + short SizeOfColorStack; + short SizeOfStyleVarStack; + short SizeOfFontStack; + short SizeOfFocusScopeStack; + short SizeOfGroupStack; + short SizeOfBeginPopupStack; + + ImGuiStackSizes() { memset(this, 0, sizeof(*this)); } + IMGUI_API void SetToCurrentState(); + IMGUI_API void CompareWithCurrentState(); +}; + //----------------------------------------------------------------------------- // [SECTION] Generic context hooks //----------------------------------------------------------------------------- @@ -1316,6 +1338,7 @@ struct ImGuiContext bool WithinFrameScope; // Set by NewFrame(), cleared by EndFrame() bool WithinFrameScopeWithImplicitWindow; // Set by NewFrame(), cleared by EndFrame() when the implicit debug window has been pushed bool WithinEndChild; // Set within EndChild() + bool GcCompactAll; // Request full GC bool TestEngineHookItems; // Will call test engine hooks: ImGuiTestEngineHook_ItemAdd(), ImGuiTestEngineHook_ItemInfo(), ImGuiTestEngineHook_Log() ImGuiID TestEngineHookIdInfo; // Will call test engine hooks: ImGuiTestEngineHook_IdInfo() from GetID() void* TestEngine; // Test engine user data @@ -1372,9 +1395,12 @@ struct ImGuiContext ImGuiNextItemData NextItemData; // Storage for SetNextItem** functions // Shared stacks - ImVector ColorModifiers; // Stack for PushStyleColor()/PopStyleColor() - ImVector StyleModifiers; // Stack for PushStyleVar()/PopStyleVar() - ImVector FontStack; // Stack for PushFont()/PopFont() + ImVector ColorStack; // Stack for PushStyleColor()/PopStyleColor() - inherited by Begin() + ImVector StyleVarStack; // Stack for PushStyleVar()/PopStyleVar() - inherited by Begin() + ImVector FontStack; // Stack for PushFont()/PopFont() - inherited by Begin() + ImVector FocusScopeStack; // Stack for PushFocusScope()/PopFocusScope() - not inherited by Begin(), unless child window + ImVectorItemFlagsStack; // Stack for PushItemFlag()/PopItemFlag() - inherited by Begin() + ImVectorGroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin() ImVectorOpenPopupStack; // Which popups are open (persistent) ImVectorBeginPopupStack; // Which level of BeginPopup() we are in (reset every frame) @@ -1546,6 +1572,7 @@ struct ImGuiContext FrameCount = 0; FrameCountEnded = FrameCountPlatformEnded = FrameCountRendered = -1; WithinFrameScope = WithinFrameScopeWithImplicitWindow = WithinEndChild = false; + GcCompactAll = false; TestEngineHookItems = false; TestEngineHookIdInfo = 0; TestEngine = NULL; @@ -1687,7 +1714,8 @@ struct ImGuiContext //----------------------------------------------------------------------------- // Transient per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the DC variable name in ImGuiWindow. -// FIXME: That's theory, in practice the delimitation between ImGuiWindow and ImGuiWindowTempData is quite tenuous and could be reconsidered. +// (That's theory, in practice the delimitation between ImGuiWindow and ImGuiWindowTempData is quite tenuous and could be reconsidered..) +// (This doesn't need a constructor because we zero-clear it as part of ImGuiWindow and all frame-temporary data are setup on Begin) struct IMGUI_API ImGuiWindowTempData { // Layout @@ -1733,48 +1761,12 @@ struct IMGUI_API ImGuiWindowTempData // Local parameters stacks // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. - ImGuiItemFlags ItemFlags; // == ItemFlagsStack.back() [empty == ImGuiItemFlags_Default] + ImGuiItemFlags ItemFlags; // == g.ItemFlagsStack.back() float ItemWidth; // == ItemWidthStack.back(). 0.0: default, >0.0: width in pixels, <0.0: align xx pixels to the right of window float TextWrapPos; // == TextWrapPosStack.back() [empty == -1.0f] - ImVectorItemFlagsStack; ImVector ItemWidthStack; ImVector TextWrapPosStack; - ImVectorGroupStack; - short StackSizesBackup[6]; // Store size of various stacks for asserting - - ImGuiWindowTempData() - { - CursorPos = CursorPosPrevLine = CursorStartPos = CursorMaxPos = ImVec2(0.0f, 0.0f); - CurrLineSize = PrevLineSize = ImVec2(0.0f, 0.0f); - CurrLineTextBaseOffset = PrevLineTextBaseOffset = 0.0f; - Indent = ImVec1(0.0f); - ColumnsOffset = ImVec1(0.0f); - GroupOffset = ImVec1(0.0f); - - LastItemId = 0; - LastItemStatusFlags = ImGuiItemStatusFlags_None; - LastItemRect = LastItemDisplayRect = ImRect(); - - NavLayerActiveMask = NavLayerActiveMaskNext = 0x00; - NavLayerCurrent = ImGuiNavLayer_Main; - NavFocusScopeIdCurrent = 0; - NavHideHighlightOneFrame = false; - NavHasScroll = false; - - MenuBarAppending = false; - MenuBarOffset = ImVec2(0.0f, 0.0f); - TreeDepth = 0; - TreeJumpToParentOnPopMask = 0x00; - StateStorage = NULL; - CurrentColumns = NULL; - LayoutType = ParentLayoutType = ImGuiLayoutType_Vertical; - FocusCounterRegular = FocusCounterTabStop = -1; - - ItemFlags = ImGuiItemFlags_Default_; - ItemWidth = 0.0f; - TextWrapPos = -1.0f; - memset(StackSizesBackup, 0, sizeof(StackSizesBackup)); - } + ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting }; // Storage for one window @@ -1838,7 +1830,7 @@ struct IMGUI_API ImGuiWindow ImVector IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack. (In theory this should be in the TempData structure) ImGuiWindowTempData DC; // Temporary per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the "DC" variable name. - // The best way to understand what those rectangles are is to use the 'Metrics -> Tools -> Show windows rectangles' viewer. + // The best way to understand what those rectangles are is to use the 'Metrics->Tools->Show Windows Rectangles' viewer. // The main 'OuterRect', omitted as a field, is window->Rect(). ImRect OuterRectClipped; // == Window->Rect() just after setup in Begin(). == window->Rect() for root window. ImRect InnerRect; // Inner rectangle (omit title bar, menu bar, scroll bar) @@ -2095,6 +2087,7 @@ namespace ImGui inline ImGuiItemStatusFlags GetItemStatusFlags() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.LastItemStatusFlags; } inline ImGuiID GetActiveID() { ImGuiContext& g = *GImGui; return g.ActiveId; } inline ImGuiID GetFocusID() { ImGuiContext& g = *GImGui; return g.NavId; } + inline ImGuiItemFlags GetItemsFlags() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.ItemFlags; } IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window); IMGUI_API void SetFocusID(ImGuiID id, ImGuiWindow* window); IMGUI_API void ClearActiveID(); @@ -2157,7 +2150,8 @@ namespace ImGui // patterns generally need to react (e.g. clear selection) when landing on an item of the set. IMGUI_API void PushFocusScope(ImGuiID id); IMGUI_API void PopFocusScope(); - inline ImGuiID GetFocusScopeID() { ImGuiContext& g = *GImGui; return g.NavFocusScopeId; } + inline ImGuiID GetFocusedFocusScope() { ImGuiContext& g = *GImGui; return g.NavFocusScopeId; } // Focus scope which is actually active + inline ImGuiID GetFocusScope() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.NavFocusScopeIdCurrent; } // Focus scope we are outputting into, set by PushFocusScope() // Inputs // FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions. @@ -2341,6 +2335,7 @@ namespace ImGui IMGUI_API void ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp); // Garbage collection + IMGUI_API void GcCompactTransientMiscBuffers(); IMGUI_API void GcCompactTransientWindowBuffers(ImGuiWindow* window); IMGUI_API void GcAwakeTransientWindowBuffers(ImGuiWindow* window); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 2546d0bb..6d3000e1 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1353,7 +1353,9 @@ void ImGui::SeparatorEx(ImGuiSeparatorFlags flags) // Horizontal Separator float x1 = window->Pos.x; float x2 = window->Pos.x + window->Size.x; - if (!window->DC.GroupStack.empty()) + + // FIXME-WORKRECT: old hack (#205) until we decide of consistent behavior with WorkRect/Indent and Separator + if (g.GroupStack.Size > 0 && g.GroupStack.back().WindowID == window->ID) x1 += window->DC.Indent.x; ImGuiColumns* columns = (flags & ImGuiSeparatorFlags_SpanAllColumns) ? window->DC.CurrentColumns : NULL; @@ -4280,10 +4282,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ IM_ASSERT(callback_data.Buf == state->TextA.Data); // Invalid to modify those fields IM_ASSERT(callback_data.BufSize == state->BufCapacityA); IM_ASSERT(callback_data.Flags == flags); - if (callback_data.CursorPos != utf8_cursor_pos) { state->Stb.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); state->CursorFollow = true; } - if (callback_data.SelectionStart != utf8_selection_start) { state->Stb.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); } - if (callback_data.SelectionEnd != utf8_selection_end) { state->Stb.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); } - if (callback_data.BufDirty) + const bool buf_dirty = callback_data.BufDirty; + if (callback_data.CursorPos != utf8_cursor_pos || buf_dirty) { state->Stb.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); state->CursorFollow = true; } + if (callback_data.SelectionStart != utf8_selection_start || buf_dirty) { state->Stb.select_start = (callback_data.SelectionStart == callback_data.CursorPos) ? state->Stb.cursor : ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); } + if (callback_data.SelectionEnd != utf8_selection_end || buf_dirty) { state->Stb.select_end = (callback_data.SelectionEnd == callback_data.SelectionStart) ? state->Stb.select_start : ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); } + if (buf_dirty) { IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! if (callback_data.BufTextLen > backup_current_text_length && is_resizable) @@ -6509,7 +6512,7 @@ void ImGui::EndMenuBar() 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.GroupStack.back().EmitItem = false; + g.GroupStack.back().EmitItem = false; EndGroup(); // Restore position on layer 0 window->DC.LayoutType = ImGuiLayoutType_Vertical; window->DC.NavLayerCurrent = ImGuiNavLayer_Main; @@ -7463,7 +7466,7 @@ static ImGuiTabItem* ImGui::TabBarScrollingButtons(ImGuiTabBar* tab_bar) { target_order += select_dir; selected_order += select_dir; - tab_to_scroll_to = (target_order <= 0 || target_order >= tab_bar->Tabs.Size) ? tab_to_scroll_to : NULL; + tab_to_scroll_to = (target_order < 0 || target_order >= tab_bar->Tabs.Size) ? tab_to_scroll_to : NULL; } } } @@ -7564,7 +7567,7 @@ void ImGui::EndTabItem() IM_ASSERT(tab_bar->LastTabItemIdx >= 0); ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx]; if (!(tab->Flags & ImGuiTabItemFlags_NoPushId)) - window->IDStack.pop_back(); + PopID(); } bool ImGui::TabItemButton(const char* label, ImGuiTabItemFlags flags)