diff --git a/3rdparty/imgui/CHANGELOG.txt b/3rdparty/imgui/CHANGELOG.txt index 7676153b38..6b9ad6b086 100644 --- a/3rdparty/imgui/CHANGELOG.txt +++ b/3rdparty/imgui/CHANGELOG.txt @@ -39,6 +39,230 @@ HOW TO UPDATE? and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users. - Please report any issue! +----------------------------------------------------------------------- + VERSION 1.92.9b (Released 2026-07-31) +----------------------------------------------------------------------- + +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.92.9b + +Changes: + +- DragXXX, SliderXXX: fixed a regression in 1.92.9 where clicking a previously + Ctrl+Clicked field would flick it to text edit mode for a frame. (#9476, #701) +- Tables: + - Fixed Y2 position of lower horizontal border (ImGuiTableFlags_BordersOuterH) + being off by one pixel when vertical ones are not also enabled. [@thedmd] + - Fixed X position of angled borders being off by half a pixel. [@thedmd] +- ImDrawData: fixed a regression in 1.92.9 where legacy `ImDrawData::CmdListsCount` was always 0. + Prefer using `ImDrawData::CmdList.Size` anyway. + + +----------------------------------------------------------------------- + VERSION 1.92.9 (Released 2026-07-25) +----------------------------------------------------------------------- + +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.92.9 + +Breaking Changes: + +- DragXXX, SliderXXX, InputScalar: with `ImGuiItemFlags_LiveEditOnInputScalar` now + defaulting to being disabled: inputting a value with the keyboard doesn't write + intermediate values to the backing variable. (#9476) + Before: DragFloat() with user typing "123" --> write back 1, then 12, then 123. + After: DragFloat() with user typing "123" --> write back 123 when validated/unfocused. + You can enable the flag back for a given scope if needed by specific widget/behavior. + Read below for details. +- TreeNode: commented out legacy name `ImGuiTreeNodeFlags_SpanTextWidth` which + was obsoleted in 1.90.7 (May 2024). Use `ImGuiTreeNodeFlags_SpanLabelWidth`. +- ColorEdit: obsoleted SetColorEditOptions() function added in 1.51 (June 2017) in favor + of directly poking to io.ConfigColorEditFlags. More consistent and easier to discover. +- Drag and Drop: commented out legacy name `ImGuiDragDropFlags_SourceAutoExpirePayload` + which obsoleted in 1.90.9 (July 2024). Use `ImGuiDragDropFlags_PayloadAutoExpire`. +- ImDrawData: marked `CmdListsCount` as obsolete (technically was obsoleted in 1.89.8). + Before: `draw_data->CmdListsCount`, After: `draw_data->CmdLists.Size`. + +Other Changes: + +- LiveEdit on/off: added `ImGuiItemFlags_LiveEditOnInputText` and `ImGuiItemFlags_LiveEditOnInputScalar` + flags to configure the timing of applying edits of backing variables when typing + values using a keyboard. + (#9476, #701, #3936, #3946, #5904, #6284, #8149, #8065, #8665, #9117, #9299, #700, #1351, + #1875, #2060, #2215, #2380, #2550, #3083, #3338, #3556, #4373, #4714, #4885, #5184, + #5777, #6707, #6766, #8004, #8303, #8915, #9308) + - ImGuiItemFlags_LiveEditOnInputText is enabled by default (same as before): + - The expectation is that for strings/text, enabling LiveEdit is a better default. + - ImGuiItemFlags_LiveEditOnInputScalar is disabled by default (new behavior): + - The expectation is that for numeric/scalars values, disabling LiveEdit is a better default. + - Until now: + - Edits where always applied immediately to backing variable, which is equivalent + to the `ImGuiItemFlags_LiveEditXXX` flags being enabled. + - Typing '123' in an integer field would output successively 1, 12 then 123. + - Most uses of `IsItemDeactivatedAfterEdit()` or `ImGuiInputTextFlags_EnterReturnsTrue` + were actually workarounds for this issue. Advanced applications would typically + use `IsItemDeactivatedAfterEdit()` to distinguish transactions. + Workarounds often required a backing store for scalar values, and there were + also a few niggles related to `IsItemDeactivatedAfterEdit()` when using +/- + buttons of an `InputInt()` widgets. + - Many of those situations can now be naturally simplified when disabling + `ImGuiItemFlags_LiveEditOnInputScalar` is disabled. + - The new flags allows disabling this behavior selectively for strings fields + such as `InputText()` vs scalar fields: `SliderInt()`, `InputFloat()`, etc. + - When LiveEdit is disabled, edits are applied when pressing enter, tabbing out, + clearing a field or deactivating due to a focus loss. + - The flag may be altered programmatically, e.g. for the entire frame scope: + NewFrame(); + PushItemFlag(ImGuiItemFlags_LiveEditOnInputScalar, true); // Enable for scalars for the whole frame + .... + PopItemFlag(); + EndFrame(); + Or for a specific widget: + PushItemFlag(ImGuiItemFlags_LiveEditOnInput, true); // Enable for one widget + SliderInt(...); + PopItemFlag(); + - We intentionally are not adding `io.ConfigLiveEditXXX` fields to dictate the + default value of each `ImGuiItemFlags_LiveEditXXX`, because this is not expected + to be a user preference but a programmer/widget preferences. + And we strive to make the toolkit consistent. +- Windows: + - Clicking on a window's empty-space to move/focus a window checks + for lack of mouse button ownership. This gives an additional opportunity + for user code to bypass it without using a clickable item. (#9382) + - Clicking on a window's empty-space to move/focus a window checks + for lack of queued focus request. (#9382) + - Fixed double-click collapse toggle not owning the mouse button. If a + `SetNextWindowPos()` with pivot was queued in the same frame, the second + click could trigger another item in the same window. (#9439) [@Cleroth] +- Popups: + - Added bool return value to `OpenPopup()`, `OpenPopupOnItemClick()` functions + to notify when the popup was just opened. (#9429) +- InputText: + - Added `style.InputTextCursorSize` to configure cursor/caret thickness. (#7031, #9409) + It is automatically scaled by `style.ScaleAllSizes()`. + - Reworked `io.ConfigInputTextEnterKeepActive` mode so that pressing + Ctrl+Enter or Shift+Enter still allows to deactivate. (#9239) +- Tables: + - Redesigned/rewrote code to reconcile columns and settings on topology changes. (#9108) + - When a column label is passed to TableSetupColumn(), the underlying identifier + is used to match live columns data and .ini settings data when changing. + This makes it possible to add/remove columns from a table without losing + neither live data neither .ini settings data. + - PS: Note that this is distinct from toggling column visibility or reordering + columns, which was always possible. The new matching makes it easier to create + tables that are entirely customized by user or code, without losing state. + - Columns without identifiers or with duplicate identifiers are matched + sequentially, matching old behavior. + - Column ID are stored in .ini file. + - Code is being tested both for live topology changes and for loading .ini + data with mismatched topology. + - Context Menu: added a "Reset" sub-menu with a "Reset Visibility" option. + (which is greyed out when using default settings) + - Headers: fixed label being clipped early to reserve space for a sort marker + even when no sort marker is displayed. Auto-fitting a column still accounts + for the possible marker, so that sorting after an auto-fit doesn't clip the label. +- Multi-Select: + - Reworked `ImGuiMultiSelectFlags_NoAutoSelect` as it carried side-effects that + were hardcoded/designed to use multi-selection on checkboxes. (#9391) + Specifically removed those undocumented behaviors from `_NoAutoSelect`: + - Clicking a selected/checked item always unselect/uncheck. + - Shift+Click inverts targets value and copy to range. + - Shift+Keyboard copy selection source value to range. + Those behaviors are still happening on checkboxes used within multi-selection. +- Fonts: + - Added `IMGUI_DISABLE_DEFAULT_FONT_BITMAP`/`IMGUI_DISABLE_DEFAULT_FONT_VECTOR` to + disable embedding either fonts separately. (#9407) + - Tweak `CalcTextSize()` awkward width rounding/ceiling code to reduce floating-point + precision issues altering the result by 1 even at relatively small width. (#791) + - Better document the fact that `ImFontAtlas::Clear()`/`ClearFonts()` functions are + unlikely to be useful nowadays. Better recover to an edge case of mistakenly + calling `ClearFonts()` during rendering. + - Fixed an issue where passing a manually created ImFontAtlas to CreateContext() would + incorrectly destroy it in `DestroyContext()` when ref-count gets back to zero. (#9426) + - Destroying an ImGui context using a `ImFontAtlas` checks that the later has no references. +- Nav: + - Fixed context menu activation with gamepad erroneously testing for _NavEnableKeyboard + instead of _NavEnableGamepad. (#9454, #8803, #9270) [@Clownacy] +- TreeNode: + - Fixed nav cursor rendering with rounding even though tree nodes don't have it. (#7589) +- Inputs: + - Added `GetItemClickedCountWithSingleClickDelay()` helper for easy disambiguation + between single-click and double-click for actions that needs single-click to do + something other than selection. (#8337) + - Returns 1 on single-click but delayed by io.MouseSingleClickDelay. + - Returns 2 on double-click, and 2+ on subsequent repeated clicks. + - Added `io.MouseSingleClickDelay` to configure default delayed single click delay when + using `GetItemClickedCountWithSingleClickDelay()` or `IsMouseReleasedWithDelay()`. (#8337) + Note that `io.MouseSingleClickDelay` is always `> io.MouseDoubleClickTime`. +- ColorEdit: + - Added `io.ConfigColorEditFlags` to read/modify current color edit/picker settings. + - ColorPicker: added `ImGuiColorEditFlags_PickerNoRotate` to disable rotation of the + S/V triangle when in `ImGuiColorEditFlags_PickerHueWheel` mode. (#9337) [@SeanTheBuilder1] + Likely best passed to `style.ColorEditFlags` to setup globally and once. + - ColorButton: small rendering tweak/optimization for the alpha checkerboard. +- Style: + - Added style.MenuItemRounding, ImGuiStyleVar_MenuItemRounding. (#7589, #9375, #9453) + - Added style.SelectableRounding, ImGuiStyleVar_SelectableRounding. (#7589, #9375, #9453) + The use of this is discouraged because it can easily create problems rendering e.g. + contiguous selection. + - Scale the NavCursor border thickness when using large values with `ScallAllSizes()`. +- Settings: + - Windows/Tables settings entries can now record the last used date in YYYYMMDD format, + allowing tools to run to e.g. delete entries that haven't been used in X months. (#9460) + - Added `bool io.ConfigIniSettingsSaveLastUsedDate` to disable saving that info. (#9460) + - Added `int io.ConfigIniSettingsAutoDiscardMonths` to enable a mode where unused settings + are automatically discard after xx months. (#9460) + - Added a trimming tool under Metrics->Settings, along with a yet-unexposed function. + - The current system date is fed through `ImGuiPlatformIO::Platform_SessionDate`, + which is automatically set by a call to time() done during context creation. (#9460) + - Added `IMGUI_DISABLE_TIME_FUNCTIONS` to disable setting platform_io.Platform_SessionDate. + A custom backend may still set it manually. (#9460) +- DrawList: + - Minor optimization to `AddLine()`, `AddLineH()`, `AddLineV()` functions. (#4091) + - Added `ImDrawListFlags_TextNoPixelSnap` to disable snapping of AddText() + coordinates for a given scope. (#3437, #9417, #2291) +- Demo: + - Extract 'Widgets->Tree Nodes->Selectable Nodes' out of the 'Advanced' + demo for clarity (manual reimplementation of basic selection). +- Debug Tools: + - Added `IM_DEBUG_BREAK()` handler for GCC+AArch64/ARM64. [@tom-seddon] +- Backends: + - Android: + - Clear mouse position on touch release (`AMOTION_EVENT_ACTION_UP`) to prevent + items from staying in hovered state. (#6627, #9474) [@Turtle-PB] + - Metal4: + - Added new Metal 4 backend (forked from Metal 3 backend). (#9458, #9451) [@AmelieHeinrich] + - Added Metal-cpp support enabled with `IMGUI_IMPL_METAL_CPP` define. (#9461) [@MERL10N] + - OpenGL2: + - Backup and restore `GL_UNPACK_ROW_LENGTH` and `GL_UNPACK_ALIGNMENT` when updating texture + to avoid altering caller GL state. (#8802, #9473) [@Turtle-PB] + - OpenGL3: + - GLSL version detection assume GLSL 410 when GL context is 4.1. + Fixes an issue running on macOS with Wine. [#9427, #6577) [@perminovVS] + - Expose selected render state in `ImGui_ImplOpenGL3_RenderState`, allowing to + dynamically select between use of `glBindSampler()` and `glTexParameter()`. (#9378) + - Backup and restore `GL_UNPACK_ROW_LENGTH` and `GL_UNPACK_ALIGNMENT` when updating texture + to avoid altering caller GL state. (#8802, #9473) [@Turtle-PB] + - SDL2: + - Restore SDL_StartTextInput()/SDL_StopTextInput() in IME handler for on-screen keyboard + support on Android. (#7636, #9474) [@Turtle-PB] + - SDLRenderer3: + - Fixed sampler change which didn't work on all graphics backends. (#7616, #9470, #9378) + - Fixed default sampler not being Linear. Regression in 1.92.8. (#7616, #9470, #9378) [@ShiroKSH] + - Win32: + - Uses `SetProcessDpiAwarenessContext()` instead of `SetThreadDpiAwarenessContext()` + when available, fixing OpenGL DPI scaling issues as e.g. NVIDIA drivers tends + to spawn multiple-thread to manage OpenGL. (#9403) +- Examples: + - Android: update to AGP 9.2.0 to support Gradle 9.6.0. + - Apple+Metal4: added new example. (#9465, #9451) [@hoffstadt] + - OpenGL3+GLFW/SDL2/SDL3: allow Wine compatibility by passing empty GLSL version + string to ImGui_ImplOpenGL3_Init() to let backend decide of a GLSL version based + on actual GL version obtained. (#9427, #6577) [@perminovVS] + - OpenGL3+Win32: rework context creation to allow Wine compatibility. (#9427, #6577) [@perminovVS] + - SDL2/SDL3: use `SDL_GetWindowSizeInPixels()` to create frame-buffers. Fixes issues + with non-fractional framebuffer size on Wayland. (#8761, #9124) [@billtran1632001] + - SDL3+Metal4: added new example. (#9458, #9451) [@AmelieHeinrich] + + ----------------------------------------------------------------------- VERSION 1.92.8 (Released 2026-05-12) ----------------------------------------------------------------------- @@ -68,9 +292,13 @@ Breaking Changes: The new order is also more convenient as `flags` are less frequently used than `thickness` in real code. - As a general policy in Dear ImGui, all our flags default to 0 so ImDrawFlags_None was likely written 0 in some call sites. - Consider adding `#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS` in your imconfig.h, even temporarily, to clean up legacy code. + - Reminder: you do NOT need to specify `ImDrawFlags_RoundCornersAll` for rounding, it is already the default! - DrawList: obsoleted `ImDrawCallback_ResetRenderState` in favor of using `ImGui::GetPlatformIO().DrawCallback_ResetRenderState`, which is part of our new standard draw callbacks. (#9378) Redirecting the earlier value into the later one when set, so both old and new code should work. +- DrawList: changed value of `ImDrawFlags_Closed`. It was previously advertised as "always == 1" when introduced + in 1.82 (2021/02), in order to facilitate backward compatibility with the legacy `bool closed` flag. This + guarantee has been removed. The bit is reserved, and `AddPolyline()`, `PathStroke()` will assert when it is used. - Backends: - Vulkan: redesigned to use separate ImageView + Sampler instead of Combined Image Sampler. (#914) This change allows us to facilitate changing samplers, in line with other backends. [@yaz0r, @ocornut] @@ -85,6 +313,8 @@ Breaking Changes: - DirectX10, DirectX11, SDLGPU3, Vulkan: removed samplers from `ImGui_ImplXXXX_RenderState`. Prefer to use backend-agnostic DrawCallback_SetSamplerLinear which works everywhere! (#9378) If there is a legit need/request for them or any render state we can always add them back. + - OpenGL3: the new sampler support system prioritize using glBindSampler() when available, + which would override glTexParameter() settings you may have set on custom textures. (#9378) Other Changes: @@ -101,6 +331,15 @@ Other Changes: #5999, #6452, #5156, #7342, #7592, #7511) - Made `AddCallback()` user data default to Null for convenience. - Added `AddLineH()`, `AddLineV()` helpers to draw horizontal and vertical lines. [@memononen] + The new functions are more optimal and will be part of a larger effort in 1.93 to fix + inconsistencies and improve the DrawList API. In the current API, `AddLine()` adds a "magic" +0.5f + offset and then center the line, whereas AddLineH()/AddLineV() use the right side of the line to expand. + For thickness=1.0f lines this is equivalent: + `AddLine({10,3}, {20,3}, ...)` --> `AddLineH(x1=10, x2=20, y=3, ...)`. + `AddLine({3,10}, {3,20}, ...)` --> `AddLineV(x=3, y1=10, y2=20, ...)`. + For larger integer thickness, AddLine() will center but may be blurry, AddLineH()/AddLineV() + will expand "inside" (to the right side) and will always be sharp. + A subsequent release will expand on those concepts and add new options. - InputText: - InputTextMultiline: fixed an issue processing deactivation logic when an active multi-line edit is clipped due to being out of view. @@ -889,7 +1128,7 @@ Other Changes: not available. (#5924, #5562) - SDL3+DirectX11: Added SDL3+DirectX11 example. (#8956, #8957) [@tomaz82] - Win32+DirectX12: Rework synchronization logic. (#8961) [@RT2Code] - - Made examples's main.cpp consistent with returning 1 on error. + - Made examples' main.cpp consistent with returning 1 on error. ----------------------------------------------------------------------- @@ -3331,8 +3570,10 @@ Breaking changes: input queue. This is automatically cleared by io.ClearInputKeys(). (#4921) - ImDrawData: CmdLists[] array is now owned, changed from 'ImDrawList**' to 'ImVector'. Majority of users shouldn't be affected, but you - cannot compare to NULL nor reassign manually anymore. - Instead use AddDrawList(). Allocation count are identical. (#6406, #4879, #1878) + cannot compare to NULL nor reassign manually anymore. (#6406, #4879, #1878) + - Allocation count are identical. + - Use `draw_data->CmdLists.Size` instead of `draw_list->CmdListsCount`! + - Use `AddDrawList()` to add to a `ImDrawData`. Other changes: diff --git a/3rdparty/imgui/CMakeLists.txt b/3rdparty/imgui/CMakeLists.txt index afaebb805c..71a8a7e436 100644 --- a/3rdparty/imgui/CMakeLists.txt +++ b/3rdparty/imgui/CMakeLists.txt @@ -5,14 +5,14 @@ add_library(imgui include/imgui_internal.h include/imgui_stdlib.h include/imstb_textedit.h + include/imstb_rectpack.h + include/imstb_truetype.h src/imgui.cpp src/imgui_demo.cpp src/imgui_draw.cpp src/imgui_freetype.cpp src/imgui_tables.cpp src/imgui_widgets.cpp - src/imstb_rectpack.h - src/imstb_truetype.h src/imgui_stdlib.cpp ) diff --git a/3rdparty/imgui/include/imconfig.h b/3rdparty/imgui/include/imconfig.h index c7ffae24ca..a758c6d211 100644 --- a/3rdparty/imgui/include/imconfig.h +++ b/3rdparty/imgui/include/imconfig.h @@ -43,17 +43,20 @@ //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a) //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME). //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). +//#define IMGUI_DISABLE_TIME_FUNCTIONS // Don't setup default platform_io.Platform_SessionDate value using time(), localtime_r(). //#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default platform_io.Platform_OpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")). //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. //#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) //#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). -//#define IMGUI_DISABLE_DEFAULT_FONT // Disable default embedded fonts (ProggyClean/ProggyForever), remove ~9 KB + ~14 KB from output binary. AddFontDefaultXXX() functions will assert. +//#define IMGUI_DISABLE_DEFAULT_FONT // Disable default embedded fonts (ProggyClean + ProggyForever). Remove ~9 KB + ~14 KB from output binary. AddFontDefaultXXX() functions will assert. +//#define IMGUI_DISABLE_DEFAULT_FONT_BITMAP // Disable default embedded bitmap font (ProggyClean). Remove ~9 KB from output binary. AddFontDefaultBitmap() will assert. +//#define IMGUI_DISABLE_DEFAULT_FONT_VECTOR // Disable default embedded vector font (ProggyForever), Remove ~14 KB from output binary. AddFontDefaultVector() will assert. //#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available //---- Enable Test Engine / Automation features. -//#define IMGUI_ENABLE_TEST_ENGINE // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_config.h", see Test Engine for details. +//#define IMGUI_ENABLE_TEST_ENGINE // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_imconfig.h", see Test Engine for details. //---- Include imgui_user.h at the end of imgui.h as a convenience // May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included. diff --git a/3rdparty/imgui/include/imgui.h b/3rdparty/imgui/include/imgui.h index 445bfeaac1..05fc3309ee 100644 --- a/3rdparty/imgui/include/imgui.h +++ b/3rdparty/imgui/include/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 +// dear imgui, v1.92.9b // (headers) // Help: @@ -29,8 +29,8 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') -#define IMGUI_VERSION "1.92.8" -#define IMGUI_VERSION_NUM 19280 +#define IMGUI_VERSION "1.92.9b" +#define IMGUI_VERSION_NUM 19291 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 @@ -243,7 +243,7 @@ typedef int ImFontAtlasFlags; // -> enum ImFontAtlasFlags_ // Flags: f typedef int ImGuiBackendFlags; // -> enum ImGuiBackendFlags_ // Flags: for io.BackendFlags typedef int ImGuiButtonFlags; // -> enum ImGuiButtonFlags_ // Flags: for InvisibleButton() typedef int ImGuiChildFlags; // -> enum ImGuiChildFlags_ // Flags: for BeginChild() -typedef int ImGuiColorEditFlags; // -> enum ImGuiColorEditFlags_ // Flags: for ColorEdit4(), ColorPicker4() etc. +typedef int ImGuiColorEditFlags; // -> enum ImGuiColorEditFlags_ // Flags: for ColorEdit4(), ColorPicker4(), io.ConfigColorEditFlags etc. typedef int ImGuiConfigFlags; // -> enum ImGuiConfigFlags_ // Flags: for io.ConfigFlags typedef int ImGuiComboFlags; // -> enum ImGuiComboFlags_ // Flags: for BeginCombo() typedef int ImGuiDragDropFlags; // -> enum ImGuiDragDropFlags_ // Flags: for BeginDragDropSource(), AcceptDragDropPayload() @@ -613,7 +613,8 @@ namespace ImGui IMGUI_API ImGuiID GetID(int int_id); // Widgets: Text - IMGUI_API void TextUnformatted(const char* text, const char* text_end = NULL); // raw text without formatting. Roughly equivalent to Text("%s", text) but: A) doesn't require null terminated string if 'text_end' is specified, B) it's faster, no memory copy is done, no buffer size limits, recommended for long chunks of text. + // - Note that all functions taking format strings in the API may be passed ("%s", text) or ("%.*s", text_len, text): which will automatically bypass the formatter. + IMGUI_API void TextUnformatted(const char* text, const char* text_end = NULL); // raw text without formatting. Practically equivalent to 'Text("%s", text)' but doesn't require null terminated string if 'text_end' is specified. IMGUI_API void Text(const char* fmt, ...) IM_FMTARGS(1); // formatted text IMGUI_API void TextV(const char* fmt, va_list args) IM_FMTLIST(1); IMGUI_API void TextColored(const ImVec4& col, const char* fmt, ...) IM_FMTARGS(2); // shortcut for PushStyleColor(ImGuiCol_Text, col); Text(fmt, ...); PopStyleColor(); @@ -648,7 +649,7 @@ namespace ImGui // Widgets: Images // - Read about ImTextureID/ImTextureRef here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples // - 'uv0' and 'uv1' are texture coordinates. Read about them from the same link above. - // - Image() pads adds style.ImageBorderSize on each side, ImageButton() adds style.FramePadding on each side. + // - Image() adds style.ImageBorderSize on each side, ImageButton() adds style.FramePadding on each side. // - ImageButton() draws a background based on regular Button() color + optionally an inner background if specified. // - An obsolete version of Image(), before 1.91.9 (March 2025), had a 'tint_col' parameter which is now supported by the ImageWithBg() function. IMGUI_API void Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1)); @@ -736,7 +737,6 @@ namespace ImGui IMGUI_API bool ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags = 0); IMGUI_API bool ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags = 0, const float* ref_col = NULL); IMGUI_API bool ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // display a color square/button, hover for details, return true when pressed. - IMGUI_API void SetColorEditOptions(ImGuiColorEditFlags flags); // initialize current options (generally on application startup) if you want to select a default format, picker type, etc. User will be able to change many settings, unless you pass the _NoOptions flag to your calls. // Widgets: Trees // - TreeNode functions return true when the node is open, in which case you need to also call TreePop() when you are finished displaying the tree node contents. @@ -808,7 +808,7 @@ namespace ImGui // - Use BeginMenuBar() on a window ImGuiWindowFlags_MenuBar to append to its menu bar. // - Use BeginMainMenuBar() to create a menu bar at the top of the screen and append to it. // - Use BeginMenu() to create a menu. You can call BeginMenu() multiple time with the same identifier to append more items to it. - // - Not that MenuItem() keyboardshortcuts are displayed as a convenience but _not processed_ by Dear ImGui at the moment. + // - Note that MenuItem() keyboard shortcuts are displayed as a convenience but _not processed_ by Dear ImGui at the moment. IMGUI_API bool BeginMenuBar(); // append to menu-bar of current window (requires ImGuiWindowFlags_MenuBar flag set on parent window). IMGUI_API void EndMenuBar(); // only call EndMenuBar() if BeginMenuBar() returns true! IMGUI_API bool BeginMainMenuBar(); // create and append to a full screen menu-bar. @@ -837,7 +837,7 @@ namespace ImGui // Popups, Modals // - They block normal mouse hovering detection (and therefore most mouse interactions) behind them. - // - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE. + // - If not modal: they can be closed by clicking anywhere outside them, or by pressing Escape (call 'Shortcut(ImGuiKey_Escape)' to claim a higher-priority shortcut). // - Their visibility state (~bool) is held internally instead of being held by the programmer as we are used to with regular Begin*() calls. // - The 3 properties above are related: we need to retain popup visibility state in the library because popups may be closed as any time. // - You can bypass the hovering restriction by using ImGuiHoveredFlags_AllowWhenBlockedByPopup when calling IsItemHovered() or IsWindowHovered(). @@ -850,15 +850,17 @@ namespace ImGui IMGUI_API void EndPopup(); // only call EndPopup() if BeginPopupXXX() returns true! // Popups: open/close functions - // - OpenPopup(): set popup state to open. ImGuiPopupFlags are available for opening options. + // - OpenPopup(): set popup state to open (unless one of the specified ImGuiPopupFlags prevent opening). + // - OpenPopupXXX() functions return true when the popup is toggled open, which allows you to capture local state if needed. + // You may also call IsWindowAppearing() inside the later BeginPopup() scope if you need to prepare/compute data for the popup. // - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE. // - CloseCurrentPopup(): use inside the BeginPopup()/EndPopup() scope to close manually. // - CloseCurrentPopup() is called by default by Selectable()/MenuItem() when activated (FIXME: need some options). // - Use ImGuiPopupFlags_NoOpenOverExistingPopup to avoid opening a popup if there's already one at the same level. This is equivalent to e.g. testing for !IsAnyPopupOpen() prior to OpenPopup(). // - Use IsWindowAppearing() after BeginPopup() to tell if a window just opened. - IMGUI_API void OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags = 0); // call to mark popup as open (don't call every frame!). - IMGUI_API void OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags = 0); // id overload to facilitate calling from nested stacks - IMGUI_API void OpenPopupOnItemClick(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 0); // helper to open popup when clicked on last item. Default to ImGuiPopupFlags_MouseButtonRight == 1. (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors) + IMGUI_API bool OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags = 0); // call to mark popup as open (don't call every frame!). + IMGUI_API bool OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags = 0); // id overload to facilitate calling from nested stacks + IMGUI_API bool OpenPopupOnItemClick(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 0); // helper to open popup when clicked on last item. Default to ImGuiPopupFlags_MouseButtonRight == 1. (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors) IMGUI_API void CloseCurrentPopup(); // manually close the popup we have begin-ed into. // Popups: Open+Begin popup combined functions helpers to create context menus. @@ -908,14 +910,15 @@ namespace ImGui IMGUI_API bool TableSetColumnIndex(int column_n); // append into the specified column. Return true when column is visible. // Tables: Headers & Columns declaration - // - Use TableSetupColumn() to specify label, resizing policy, default width/weight, id, various other flags etc. + // - Use TableSetupColumn() to specify label, resizing policy, default width/weight, various other flags etc. + // (the trailing 'ImGuiID user_data', which used to be referred to as 'ImGuiID user_id', is merely user data that is blindly copied in ImGuiTableColumnSortSpecs). // - Use TableHeadersRow() to create a header row and automatically submit a TableHeader() for each column. // Headers are required to perform: reordering, sorting, and opening the context menu. // The context menu can also be made available in columns body using ImGuiTableFlags_ContextMenuInBody. // - You may manually submit headers using TableNextRow() + TableHeader() calls, but this is only useful in // some advanced use cases (e.g. adding custom widgets in header row). // - Use TableSetupScrollFreeze() to lock columns/rows so they stay visible when scrolled. When freezing columns you would usually also use ImGuiTableColumnFlags_NoHide on them. - IMGUI_API void TableSetupColumn(const char* label, ImGuiTableColumnFlags flags = 0, float init_width_or_weight = 0.0f, ImGuiID user_id = 0); + IMGUI_API void TableSetupColumn(const char* label, ImGuiTableColumnFlags flags = 0, float init_width_or_weight = 0.0f, ImGuiID user_data = 0); IMGUI_API void TableSetupScrollFreeze(int cols, int rows); // lock columns/rows so they stay visible when scrolled. IMGUI_API void TableHeader(const char* label); // submit one header cell manually (rarely used) IMGUI_API void TableHeadersRow(); // submit a row with headers cells based on data provided to TableSetupColumn() + submit context menu @@ -1024,6 +1027,7 @@ namespace ImGui IMGUI_API ImVec2 GetItemRectMax(); // get lower-right bounding rectangle of the last item (screen space) IMGUI_API ImVec2 GetItemRectSize(); // get size of last item IMGUI_API ImGuiItemFlags GetItemFlags(); // get generic flags of last item + IMGUI_API int GetItemClickedCountWithSingleClickDelay(ImGuiMouseButton mouse_button = 0, float delay = -1.0f); // [BETA] building block for disambiguation between single-click and double-click. Returns 1 on single-click but delayed by io.MouseSingleClickDelay after mouse release. Returns 2+ on double-click or repeated clicks. // Viewports // - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows. @@ -1106,7 +1110,7 @@ namespace ImGui IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, bool repeat = false); // did mouse button clicked? (went from !Down to Down). Same as GetMouseClickedCount() == 1. IMGUI_API bool IsMouseReleased(ImGuiMouseButton button); // did mouse button released? (went from Down to !Down) IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button); // did mouse button double-clicked? Same as GetMouseClickedCount() == 2. (note that a double-click will also report IsMouseClicked() == true) - IMGUI_API bool IsMouseReleasedWithDelay(ImGuiMouseButton button, float delay); // delayed mouse release (use very sparingly!). Generally used with 'delay >= io.MouseDoubleClickTime' + combined with a 'io.MouseClickedLastCount==1' test. This is a very rarely used UI idiom, but some apps use this: e.g. MS Explorer single click on an icon to rename. + IMGUI_API bool IsMouseReleasedWithDelay(ImGuiMouseButton button, float delay=-1.f);// delayed mouse release. Use sparingly. Prefer higher-level helper GetItemClickedCountWithSingleClickDelay(). Generally used with 'delay >= io.MouseDoubleClickTime' + combined with a 'io.MouseClickedLastCount==1' test. IMGUI_API int GetMouseClickedCount(ImGuiMouseButton button); // return the number of successive mouse-clicks at the time where a click happen (otherwise 0). IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true);// is mouse hovering given bounding rect (in screen space). clipped by current clipping settings, but disregarding of other consideration of focus/window ordering/popup-block. IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // by convention we use (-FLT_MAX,-FLT_MAX) to denote that there is no mouse available @@ -1236,14 +1240,27 @@ enum ImGuiChildFlags_ // (Those are shared by all submitted items) enum ImGuiItemFlags_ { - ImGuiItemFlags_None = 0, // (Default) + ImGuiItemFlags_None = 0, // Default: ImGuiItemFlags_NoTabStop = 1 << 0, // false // Disable keyboard tabbing. This is a "lighter" version of ImGuiItemFlags_NoNav. - ImGuiItemFlags_NoNav = 1 << 1, // false // Disable any form of focusing (keyboard/gamepad directional navigation and SetKeyboardFocusHere() calls). + ImGuiItemFlags_NoNav = 1 << 1, // false // Disable any form of focusing: keyboard/gamepad directional navigation and SetKeyboardFocusHere() calls. ImGuiItemFlags_NoNavDefaultFocus = 1 << 2, // false // Disable item being a candidate for default focus (e.g. used by title bar items). ImGuiItemFlags_ButtonRepeat = 1 << 3, // false // Any button-like behavior will have repeat mode enabled (based on io.KeyRepeatDelay and io.KeyRepeatRate values). Note that you can also call IsItemActive() after any button to tell if it is being held. ImGuiItemFlags_AutoClosePopups = 1 << 4, // true // MenuItem()/Selectable() automatically close their parent popup window. ImGuiItemFlags_AllowDuplicateId = 1 << 5, // false // Allow submitting an item with the same identifier as an item already submitted this frame without triggering a warning tooltip if io.ConfigDebugHighlightIdConflicts is set. ImGuiItemFlags_Disabled = 1 << 6, // false // [Internal] Disable interactions. DOES NOT affect visuals. This is used by BeginDisabled()/EndDisabled() and only provided here so you can read back via GetItemFlags(). + + //--------------------------------------------------------------------------------- + // LiveEdit refers to applying edits to backing variables _while_ typing a value using the keyboard. + // Widget: | Input: | w/ LiveEdit: | Output: + // InputText() | "123" | on(default) | "1" then "12" then "123" + // InputText() | "123" | off | "123" after validating or tabbing out or losing focus. + // DragFloat(), SliderInt(), etc. | "123" | on* | 1 then 12 then 123 + // DragFloat(), SliderInt(), etc. | "123" | off(default)* | 123 after validation or tabbing out or losing focus. + // (*) Since 1.92.9 (July 2026), ImGuiItemFlags_LiveEditOnInputScalar is OFF by default. In prior version it was ON for everything. + //--------------------------------------------------------------------------------- + ImGuiItemFlags_LiveEditOnInputText = 1 << 7, // true // InputText: apply keyboard edits to backing value while typing. Otherwise, edits are applied when validating, tabbing out or losing focus. + ImGuiItemFlags_LiveEditOnInputScalar = 1 << 8, // false // DragXXX, SliderXXX, InputScalar: apply keyboard edits to backing value while typing. Otherwise, edits are applied when validating, tabbing out or losing focus. + ImGuiItemFlags_LiveEditOnInput = ImGuiItemFlags_LiveEditOnInputText | ImGuiItemFlags_LiveEditOnInputScalar, }; // Flags for ImGui::InputText() @@ -1260,7 +1277,7 @@ enum ImGuiInputTextFlags_ // Inputs ImGuiInputTextFlags_AllowTabInput = 1 << 5, // Pressing TAB input a '\t' character into the text field - ImGuiInputTextFlags_EnterReturnsTrue = 1 << 6, // Return 'true' when Enter is pressed (as opposed to every time the value was modified). Consider using IsItemDeactivatedAfterEdit() instead! + ImGuiInputTextFlags_EnterReturnsTrue = 1 << 6, // Return 'true' when Enter is pressed (as opposed to every time the value was modified). Consider disabling LiveEdit! or using IsItemDeactivatedAfterEdit() instead! ImGuiInputTextFlags_EscapeClearsAll = 1 << 7, // Escape key clears content if not empty, and deactivate otherwise (contrast to default behavior of Escape to revert) ImGuiInputTextFlags_CtrlEnterForNewLine = 1 << 8, // In multi-line mode: validate with Enter, add new line with Ctrl+Enter (default is opposite: validate with Ctrl+Enter, add line with Enter). Note that Shift+Enter always enter a new line either way. @@ -1273,6 +1290,7 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_DisplayEmptyRefVal = 1 << 14, // InputFloat(), InputInt(), InputScalar() etc. only: when value is zero, do not display it. Generally used with ImGuiInputTextFlags_ParseEmptyRefVal. ImGuiInputTextFlags_NoHorizontalScroll = 1 << 15, // Disable following the cursor horizontally ImGuiInputTextFlags_NoUndoRedo = 1 << 16, // Disable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID(). + //ImGuiInputTextFlags_NoLiveEdit = 1 << 25, // Disable applying output to backing variable while typing. Same as setting ImGuiItemFlags_LiveEditOnInput to false. // Elide display / Alignment ImGuiInputTextFlags_ElideLeft = 1 << 17, // When text doesn't fit, elide left side to ensure right side stays visible. Useful for path/filenames. Single-line only! @@ -1330,7 +1348,7 @@ enum ImGuiTreeNodeFlags_ #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImGuiTreeNodeFlags_NavLeftJumpsBackHere = ImGuiTreeNodeFlags_NavLeftJumpsToParent, // Renamed in 1.92.0 - ImGuiTreeNodeFlags_SpanTextWidth = ImGuiTreeNodeFlags_SpanLabelWidth, // Renamed in 1.90.7 + //ImGuiTreeNodeFlags_SpanTextWidth = ImGuiTreeNodeFlags_SpanLabelWidth, // Renamed in 1.90.7 //ImGuiTreeNodeFlags_AllowItemOverlap = ImGuiTreeNodeFlags_AllowOverlap, // Renamed in 1.89.7 #endif }; @@ -1501,7 +1519,7 @@ enum ImGuiDragDropFlags_ ImGuiDragDropFlags_AcceptPeekOnly = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect, // For peeking ahead and inspecting the payload before delivery. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - ImGuiDragDropFlags_SourceAutoExpirePayload = ImGuiDragDropFlags_PayloadAutoExpire, // Renamed in 1.90.9 + //ImGuiDragDropFlags_SourceAutoExpirePayload = ImGuiDragDropFlags_PayloadAutoExpire, // Renamed in 1.90.9 #endif }; @@ -1854,6 +1872,8 @@ enum ImGuiStyleVar_ ImGuiStyleVar_TableAngledHeadersTextAlign,// ImVec2 TableAngledHeadersTextAlign ImGuiStyleVar_TreeLinesSize, // float TreeLinesSize ImGuiStyleVar_TreeLinesRounding, // float TreeLinesRounding + ImGuiStyleVar_MenuItemRounding, // float MenuItemRounding + ImGuiStyleVar_SelectableRounding, // float SelectableRounding ImGuiStyleVar_DragDropTargetRounding, // float DragDropTargetRounding ImGuiStyleVar_ButtonTextAlign, // ImVec2 ButtonTextAlign ImGuiStyleVar_SelectableTextAlign, // ImVec2 SelectableTextAlign @@ -1900,7 +1920,8 @@ enum ImGuiColorEditFlags_ ImGuiColorEditFlags_AlphaNoBg = 1 << 13, // // ColorEdit, ColorPicker, ColorButton: disable rendering a checkerboard background behind transparent color. ImGuiColorEditFlags_AlphaPreviewHalf= 1 << 14, // // ColorEdit, ColorPicker, ColorButton: display half opaque / half transparent preview. - // User Options (right-click on widget to change some of them). + // User Options (right-click on widget to change some of them) + // Current settings are stored in style.ColorEditFlags. ImGuiColorEditFlags_AlphaBar = 1 << 18, // // ColorEdit, ColorPicker: show vertical alpha bar/gradient in picker. ImGuiColorEditFlags_HDR = 1 << 19, // // (WIP) ColorEdit: Currently only disable 0.0f..1.0f limits in RGBA edition (note: you probably want to use ImGuiColorEditFlags_Float flag as well). ImGuiColorEditFlags_DisplayRGB = 1 << 20, // [Display] // ColorEdit: override _display_ type among RGB/HSV/Hex. ColorPicker: select any combination using one or more of RGB/HSV/Hex. @@ -1910,11 +1931,13 @@ enum ImGuiColorEditFlags_ ImGuiColorEditFlags_Float = 1 << 24, // [DataType] // ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0.0f..1.0f floats instead of 0..255 integers. No round-trip of value via integers. ImGuiColorEditFlags_PickerHueBar = 1 << 25, // [Picker] // ColorPicker: bar for Hue, rectangle for Sat/Value. ImGuiColorEditFlags_PickerHueWheel = 1 << 26, // [Picker] // ColorPicker: wheel for Hue, triangle for Sat/Value. - ImGuiColorEditFlags_InputRGB = 1 << 27, // [Input] // ColorEdit, ColorPicker: input and output data in RGB format. - ImGuiColorEditFlags_InputHSV = 1 << 28, // [Input] // ColorEdit, ColorPicker: input and output data in HSV format. + ImGuiColorEditFlags_PickerNoRotate = 1 << 27, // [Picker] // ColorPicker: disable rotating Sat/Value triangle. Best set in io.ConfigColorEditFlags once. + ImGuiColorEditFlags_InputRGB = 1 << 28, // [Input] // ColorEdit, ColorPicker: input and output data in RGB format. + ImGuiColorEditFlags_InputHSV = 1 << 29, // [Input] // ColorEdit, ColorPicker: input and output data in HSV format. - // Defaults Options. You can set application defaults using SetColorEditOptions(). The intent is that you probably don't want to - // override them in most of your calls. Let the user choose via the option menu and/or call SetColorEditOptions() once during startup. + // Defaults Options copied to io.ConfigColorEditFlags during initialization. + // The intent is that you probably don't want to override them in most of your calls. + // Let the user choose via the option menu and/or modify io.ConfigColorEditFlags directly during startup if you want. ImGuiColorEditFlags_DefaultOptions_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_PickerHueBar, // [Internal] Masks @@ -1927,8 +1950,8 @@ enum ImGuiColorEditFlags_ // Obsolete names #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImGuiColorEditFlags_AlphaPreview = 0, // Removed in 1.91.8. This is the default now. Will display a checkerboard unless ImGuiColorEditFlags_AlphaNoBg is set. -#endif //ImGuiColorEditFlags_RGB = ImGuiColorEditFlags_DisplayRGB, ImGuiColorEditFlags_HSV = ImGuiColorEditFlags_DisplayHSV, ImGuiColorEditFlags_HEX = ImGuiColorEditFlags_DisplayHex // [renamed in 1.69] +#endif }; // Flags for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc. @@ -1946,6 +1969,8 @@ enum ImGuiSliderFlags_ ImGuiSliderFlags_NoSpeedTweaks = 1 << 11, // Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic. ImGuiSliderFlags_ColorMarkers = 1 << 12, // DragScalarN(), SliderScalarN(): Draw R/G/B/A color markers on each component. ImGuiSliderFlags_AlwaysClamp = ImGuiSliderFlags_ClampOnInput | ImGuiSliderFlags_ClampZeroRange, + //ImGuiSliderFlags_LiveEditOnInput = 1 << 13, // Shortcut to enable LiveEdit for this field. + //ImGuiSliderFlags_NoLiveEditOnInput= 1 << 14, // Shortcut to disable LiveEdit for this field. ImGuiSliderFlags_InvalidMask_ = 0x7000000F, // [Internal] We treat using those bits as being potentially a 'float power' argument from legacy API (obsoleted 2020-08) that has got miscast to this enum, and will trigger an assert if needed. }; @@ -2158,7 +2183,7 @@ struct ImGuiTableSortSpecs // Sorting specification for one column of a table (sizeof == 12 bytes) struct ImGuiTableColumnSortSpecs { - ImGuiID ColumnUserID; // User id of the column (if specified by a TableSetupColumn() call) + ImGuiID ColumnUserID; // User data for the column (if specified by a TableSetupColumn() call in the 'ImGuiID user_data' field). FIXME: Should be called 'UserData'.. ImS16 ColumnIndex; // Index of the column ImS16 SortOrder; // Index within parent ImGuiTableSortSpecs (always stored in order starting from 0, tables sorted on a single criteria will always have a 0 here) ImGuiSortDirection SortDirection; // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending @@ -2331,6 +2356,8 @@ struct ImGuiStyle ImGuiTreeNodeFlags TreeLinesFlags; // Default way to draw lines connecting TreeNode hierarchy. ImGuiTreeNodeFlags_DrawLinesNone or ImGuiTreeNodeFlags_DrawLinesFull or ImGuiTreeNodeFlags_DrawLinesToNodes. float TreeLinesSize; // Thickness of outlines when using ImGuiTreeNodeFlags_DrawLines. float TreeLinesRounding; // Radius of lines connecting child nodes to the vertical line. + float MenuItemRounding; // Radius of MenuItem, BeginMenu rounding. + float SelectableRounding; // Radius of Selectable rounding. MODIFYING THIS IS DISCOURAGED. CONTIGUOUS SELECTIONS WILL NOT LOOK RIGHT. (#7589) float DragDropTargetRounding; // Radius of the drag and drop target frame. When <0.0f: use FrameRounding. float DragDropTargetBorderSize; // Thickness of the drag and drop target border. float DragDropTargetPadding; // Size to expand the drag and drop target from actual target item size. @@ -2338,6 +2365,7 @@ struct ImGuiStyle ImGuiDir ColorButtonPosition; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f, 0.5f) (centered). ImVec2 SelectableTextAlign; // Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. + float InputTextCursorSize; // Thickness of cursor/caret in InputText(). float SeparatorSize; // Thickness of border in Separator(). Must be >= 1.0f. float SeparatorTextBorderSize; // Thickness of border in SeparatorText() ImVec2 SeparatorTextAlign; // Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center). @@ -2428,24 +2456,33 @@ struct ImGuiIO bool ConfigNavCursorVisibleAuto; // = true // Using directional navigation key makes the cursor visible. Mouse click hides the cursor. bool ConfigNavCursorVisibleAlways; // = false // Navigation cursor is always visible. - // Miscellaneous options - // (you can visualize and interact with all options in 'Demo->Configuration') - bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by backend implementations. + // Widget options bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // Swap Cmd<>Ctrl keys + OS X style text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl. bool ConfigInputTrickleEventQueue; // = true // Enable input queue trickling: some types of events submitted during the same frame (e.g. button down + up) will be spread over multiple frames, improving interactions with low framerates. bool ConfigInputTextCursorBlink; // = true // Enable blinking cursor (optional as some users consider it to be distracting). - bool ConfigInputTextEnterKeepActive; // = false // [BETA] Pressing Enter will reactivate item and select all text (single-line only). + bool ConfigInputTextEnterKeepActive; // = false // [BETA] Pressing Enter will reactivate item and select all text (single-line only). Ctrl+Enter or Shift+Enter will deactivate normally. + ImGuiColorEditFlags ConfigColorEditFlags; // = // Current settings for ColorEdit/ColorPicker widgets. Must have one bit of ImGuiColorEditFlags_DisplayMask_, one bit of ImGuiColorEditFlags_DataTypeMask_, one bit of ImGuiColorEditFlags_PickerMask_, one bit of ImGuiColorEditFlags_InputMask_. Defaults to ImGuiColorEditFlags_DefaultOptions_. May be further edited by users, unless you also set ImGuiColorEditFlags_NoOptions. bool ConfigDragClickToInputText; // = false // [BETA] Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving). Not desirable on devices without a keyboard. bool ConfigWindowsResizeFromEdges; // = true // Enable resizing of windows from their edges and from the lower-left corner. This requires ImGuiBackendFlags_HasMouseCursors for better mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag) bool ConfigWindowsMoveFromTitleBarOnly; // = false // Enable allowing to move windows only when clicking on their title bar. Does not apply to windows without a title bar. bool ConfigWindowsCopyContentsWithCtrlC; // = false // [EXPERIMENTAL] Ctrl+C copy the contents of focused window into the clipboard. Experimental because: (1) has known issues with nested Begin/End pairs (2) text output quality varies (3) text output is in submission order rather than spatial order. bool ConfigScrollbarScrollByPage; // = true // Enable scrolling page by page when clicking outside the scrollbar grab. When disabled, always scroll to clicked location. When enabled, Shift+Click scrolls to clicked location. + + // Ini Settings options + bool ConfigIniSettingsSaveLastUsedDate;// = true // Enable loading/saving last used day (YYYYMMDD) in some .ini struct, making things easier to audit and allowing custom tools to cleanup old data. + int ConfigIniSettingsAutoDiscardMonths; // = 0 // [BETA] Set number of months after which unused .ini entries are discarded on load. Require platform_io.Platform_SessionDate to be set. For systems supporting the feature, .ini entries without a LastUsed field will always be discarded! Please report if you are using this. + bool ConfigDebugIniSettings; // = false // Save .ini data with extra comments (particularly helpful for Docking, but makes saving slower) + + // Miscellaneous options + // (you can visualize and interact with all options in 'Demo->Configuration') + bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by backend implementations. float ConfigMemoryCompactTimer; // = 60.0f // Timer (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable. // Inputs Behaviors // (other variables, ones which are expected to be tweaked within UI code, are exposed in ImGuiStyle) - float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds. - float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels. + float MouseDoubleClickTime; // = 0.30f // Time for consecutive clicks to account as a double-click, in seconds. + float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click or multiple clicks, in pixels. + float MouseSingleClickDelay; // = 0.60f // Time for a delayed click when using GetItemClickedCountWithSingleClickDelay() or IsMouseReleasedWithDelay(), in seconds. Must be > io.MouseDoubleClickTime. float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging. float KeyRepeatDelay; // = 0.275f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.). float KeyRepeatRate; // = 0.050f // When holding a key/button, rate at which it repeats, in seconds. @@ -2498,9 +2535,6 @@ struct ImGuiIO // - Backends may have other side-effects on focus loss, so this will reduce side-effects but not necessary remove all of them. bool ConfigDebugIgnoreFocusLoss; // = false // Ignore io.AddFocusEvent(false), consequently not calling io.ClearInputKeys()/io.ClearInputMouse() in input processing. - // Option to audit .ini data - bool ConfigDebugIniSettings; // = false // Save .ini data with extra comments (particularly helpful for Docking, but makes saving slower) - //------------------------------------------------------------------ // Platform Identifiers // (the imgui_impl_xxxx backend files are setting those up for you) @@ -2612,7 +2646,7 @@ struct ImGuiIO //void* ImeWindowHandle; // [Obsoleted in 1.87] Set ImGuiViewport::PlatformHandleRaw instead. Set this to your HWND to get automatic IME cursor positioning. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - float FontGlobalScale; // Moved io.FontGlobalScale to style.FontScaleMain in 1.92 (June 2025) + float FontGlobalScale; // Moved io.FontGlobalScale to style.FontScaleMain in 1.92.0 (June 2025) // Legacy: before 1.91.1, clipboard functions were stored in ImGuiIO instead of ImGuiPlatformIO. // As this is will affect all users of custom engines/backends, we are providing proper legacy redirection (will obsolete). @@ -3049,6 +3083,7 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_NavWrapX = 1 << 16, // [Temporary] Enable navigation wrapping on X axis. Provided as a convenience because we don't have a design for the general Nav API for this yet. When the more general feature be public we may obsolete this flag in favor of new one. ImGuiMultiSelectFlags_NoSelectOnRightClick = 1 << 17, // Disable default right-click processing, which selects item on mouse down, and is designed for context-menus. ImGuiMultiSelectFlags_SelectOnMask_ = ImGuiMultiSelectFlags_SelectOnAuto | ImGuiMultiSelectFlags_SelectOnClickAlways | ImGuiMultiSelectFlags_SelectOnClickRelease, + ImGuiMultiSelectFlags_CheckboxMode_ = 1 << 20, // [Internal] // Obsolete names #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS @@ -3245,20 +3280,27 @@ struct ImDrawListSplitter enum ImDrawFlags_ { ImDrawFlags_None = 0, - ImDrawFlags_RoundCornersTopLeft = 1 << 4, // AddRect(), AddRectFilled(), PathRect(): enable rounding top-left corner only (when rounding > 0.0f, we default to all corners). Was 0x01. - ImDrawFlags_RoundCornersTopRight = 1 << 5, // AddRect(), AddRectFilled(), PathRect(): enable rounding top-right corner only (when rounding > 0.0f, we default to all corners). Was 0x02. - ImDrawFlags_RoundCornersBottomLeft = 1 << 6, // AddRect(), AddRectFilled(), PathRect(): enable rounding bottom-left corner only (when rounding > 0.0f, we default to all corners). Was 0x04. - ImDrawFlags_RoundCornersBottomRight = 1 << 7, // AddRect(), AddRectFilled(), PathRect(): enable rounding bottom-right corner only (when rounding > 0.0f, we default to all corners). Wax 0x08. - ImDrawFlags_RoundCornersNone = 1 << 8, // AddRect(), AddRectFilled(), PathRect(): disable rounding on all corners (when rounding > 0.0f). This is NOT zero, NOT an implicit flag! - ImDrawFlags_Closed = 1 << 9, // PathStroke(), AddPolyline(): specify that shape should be closed (Important: this is always == 1 for legacy reason) + + // Rounding for AddRect(), AddRectFilled(), PathRect() + // - When not specified, we defaults to ImDrawFlags_RoundCornersAll! So you only need to use those flags if you want another configuration. + ImDrawFlags_RoundCornersTopLeft = 1 << 4, // Round top-left corner only (when rounding > 0.0f, we default to all corners). + ImDrawFlags_RoundCornersTopRight = 1 << 5, // Round top-right corner only (when rounding > 0.0f, we default to all corners). + ImDrawFlags_RoundCornersBottomLeft = 1 << 6, // Round bottom-left corner only (when rounding > 0.0f, we default to all corners). + ImDrawFlags_RoundCornersBottomRight = 1 << 7, // Round bottom-right corner only (when rounding > 0.0f, we default to all corners). + ImDrawFlags_RoundCornersNone = 1 << 8, // Disable rounding even if `float rounding > 0.0f`. This is NOT zero, NOT an implicit flag! + ImDrawFlags_RoundCornersAll = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight | ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight, // (Default!!) + ImDrawFlags_RoundCornersDefault_ = ImDrawFlags_RoundCornersAll, // Default to ALL corners if none of the _RoundCornersXX flags are specified! ImDrawFlags_RoundCornersTop = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight, ImDrawFlags_RoundCornersBottom = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight, ImDrawFlags_RoundCornersLeft = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersTopLeft, ImDrawFlags_RoundCornersRight = ImDrawFlags_RoundCornersBottomRight | ImDrawFlags_RoundCornersTopRight, - ImDrawFlags_RoundCornersAll = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight | ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight, - ImDrawFlags_RoundCornersDefault_ = ImDrawFlags_RoundCornersAll, // Default to ALL corners if none of the _RoundCornersXX flags are specified. ImDrawFlags_RoundCornersMask_ = ImDrawFlags_RoundCornersAll | ImDrawFlags_RoundCornersNone, - ImDrawFlags_InvalidMask_ = (ImDrawFlags)0x8000000F, + + // Stroke options + ImDrawFlags_Closed = 1 << 9, // PathStroke(), AddPolyline(): specify that shape should be closed. + //ImDrawFlags_Closed = 1, // Prior to 1.92.8 (May 2026), ImDrawFlags_Closed was guaranteed to be == 1<<0 == 1 for legacy compatibility reason. Hardcoded use of 1 or true should be replaced. + + ImDrawFlags_InvalidMask_ = ~0x7FFFFFF0, // == 0x8000000F, }; // Flags for ImDrawList instance. Those are set automatically by ImGui:: functions from ImGuiIO settings, and generally not manipulated directly. @@ -3270,6 +3312,7 @@ enum ImDrawListFlags_ ImDrawListFlags_AntiAliasedLinesUseTex = 1 << 1, // Enable anti-aliased lines/borders using textures when possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). ImDrawListFlags_AntiAliasedFill = 1 << 2, // Enable anti-aliased edge around filled shapes (rounded rectangles, circles). ImDrawListFlags_AllowVtxOffset = 1 << 3, // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled. + ImDrawListFlags_TextNoPixelSnap = 1 << 4, // Disable automatically snapping AddText() calls to pixel boundaries. }; // Draw command list @@ -3421,8 +3464,8 @@ struct ImDrawList inline void PushTextureID(ImTextureRef tex_ref) { PushTexture(tex_ref); } // RENAMED in 1.92.0 inline void PopTextureID() { PopTexture(); } // RENAMED in 1.92.0 #else - IMGUI_API void AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding /*= 0.0f*/, ImDrawFlags flags /*= 0*/, float thickness /*= 1.0f*/) = delete; - IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness) = delete; + void AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding /*= 0.0f*/, ImDrawFlags flags /*= 0*/, float thickness /*= 1.0f*/) = delete; + void AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness) = delete; inline void PathStroke(ImU32 col, ImDrawFlags flags /*= 0*/, float thickness /*= 1.0f*/) = delete; #endif //inline void AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f) { AddEllipse(center, ImVec2(radius_x, radius_y), col, rot, num_segments, thickness); } // OBSOLETED in 1.90.5 (Mar 2024) @@ -3452,7 +3495,7 @@ struct ImDrawList struct ImDrawData { bool Valid; // Only valid after Render() is called and before the next NewFrame() is called. - int CmdListsCount; // == CmdLists.Size. (OBSOLETE: exists for legacy reasons). Number of ImDrawList* to render. + int FrameCount; // Frame counter of the emitter context. Mostly for debugging purpose. int TotalIdxCount; // For convenience, sum of all ImDrawList's IdxBuffer.Size int TotalVtxCount; // For convenience, sum of all ImDrawList's VtxBuffer.Size ImVector CmdLists; // Array of ImDrawList* to render. The ImDrawLists are owned by ImGuiContext and only pointed to from here. @@ -3462,6 +3505,10 @@ struct ImDrawData ImGuiViewport* OwnerViewport; // Viewport carrying the ImDrawData instance, might be of use to the renderer (generally not). ImVector* Textures; // List of textures to update. Most of the times the list is shared by all ImDrawData, has only 1 texture and it doesn't need any update. This almost always points to ImGui::GetPlatformIO().Textures[]. May be overridden or set to NULL if you want to manually update textures. +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + int CmdListsCount; // Use CmdLists.Size instead. Number of ImDrawList* to render. +#endif + // Functions ImDrawData() { Clear(); } IMGUI_API void Clear(); @@ -3503,7 +3550,7 @@ enum ImTextureStatus // You may use ImTextureData::Updates[] for the list, or ImTextureData::UpdateBox for a single bounding box. struct ImTextureRect { - unsigned short x, y; // Upper-left coordinates of rectangle to update + unsigned short x, y; // Upper-left coordinates of rectangle to update, within the parent Pixels[] array. unsigned short w, h; // Size of rectangle to update (in pixels) }; @@ -3513,19 +3560,20 @@ struct ImTextureRect // Why does we store two identifiers: TexID and BackendUserData? // - ImTextureID TexID = lower-level identifier stored in ImDrawCmd. ImDrawCmd can refer to textures not created by the backend, and for which there's no ImTextureData. // - void* BackendUserData = higher-level opaque storage for backend own book-keeping. Some backends may have enough with TexID and not need both. - // In columns below: who reads/writes each fields? 'r'=read, 'w'=write, 'core'=main library, 'backend'=renderer backend +// In columns below: who reads/writes each fields? 'r'=read, 'w'=write, 'core'=main library, 'backend'=renderer backend struct ImTextureData { //------------------------------------------ core / backend --------------------------------------- int UniqueID; // w - // [DEBUG] Sequential index to facilitate identifying a texture when debugging/printing. Unique per atlas. ImTextureStatus Status; // rw rw // ImTextureStatus_OK/_WantCreate/_WantUpdates/_WantDestroy. Always use SetStatus() to modify! void* BackendUserData; // - rw // Convenience storage for backend. Some backends may have enough with TexID. + void* QueueUserData; // r - // Convenience storage for a staged/multi-threaded rendering texture queue (e.g. imgui_threaded_rendering.h. See #8597). When != NULL, core assumes the texture is referenced by the queue. ImTextureID TexID; // r w // Backend-specific texture identifier. Always use SetTexID() to modify! The identifier will stored in ImDrawCmd::GetTexID() and passed to backend's RenderDrawData function. ImTextureFormat Format; // w r // ImTextureFormat_RGBA32 (default) or ImTextureFormat_Alpha8 int Width; // w r // Texture width int Height; // w r // Texture height int BytesPerPixel; // w r // 4 or 1 - unsigned char* Pixels; // w r // Pointer to buffer holding 'Width*Height' pixels and 'Width*Height*BytesPerPixels' bytes. + unsigned char* Pixels; // w r // Pointer to whole texture buffer holding 'Width*Height' pixels and 'Width*Height*BytesPerPixels' bytes. ImTextureRect UsedRect; // w r // Bounding box encompassing all past and queued Updates[]. ImTextureRect UpdateRect; // w r // Bounding box encompassing all queued Updates[]. ImVector Updates; // w r // Array of individual updates. @@ -3535,7 +3583,7 @@ struct ImTextureData bool WantDestroyNextFrame; // rw - // [Internal] Queued to set ImTextureStatus_WantDestroy next frame. May still be used in the current frame. // Functions - // - If GetPixels() functions asserts while being called by your render loop, it could be caused by calling ImFontAtlas::Clear() instead of ClearFonts()? + // - If GetPixels() functions asserts while being called by your render loop, it could be caused by calling ImFontAtlas::Clear()/ClearFonts()? ImTextureData() { memset((void*)this, 0, sizeof(*this)); Status = ImTextureStatus_Destroyed; TexID = ImTextureID_Invalid; } ~ImTextureData() { DestroyPixels(); } IMGUI_API void Create(ImTextureFormat format, int w, int h); @@ -3690,14 +3738,16 @@ struct ImFontAtlas IMGUI_API ImFont* AddFontFromMemoryTTF(void* font_data, int font_data_size, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // Note: Transfer ownership of 'ttf_data' to ImFontAtlas! Will be deleted after destruction of the atlas. Set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed. IMGUI_API ImFont* AddFontFromMemoryCompressedTTF(const void* compressed_font_data, int compressed_font_data_size, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data' still owned by caller. Compress with binary_to_compressed_c.cpp. IMGUI_API ImFont* AddFontFromMemoryCompressedBase85TTF(const char* compressed_font_data_base85, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data_base85' still owned by caller. Compress with binary_to_compressed_c.cpp with -base85 parameter. - IMGUI_API void RemoveFont(ImFont* font); - - IMGUI_API void Clear(); // Clear everything (fonts + textures). Don't call mid-frame! - IMGUI_API void ClearFonts(); // Clear input+output font data/glyphs. You can call this mid-frame if you load new fonts afterwards! - IMGUI_API void CompactCache(); // Compact cached glyphs and texture. + IMGUI_API void RemoveFont(ImFont* font); // Remove a font + IMGUI_API void CompactCache(); // Compact cached glyphs and texture. IMGUI_API void SetFontLoader(const ImFontLoader* font_loader); // Change font loader at runtime. - // As we are transitioning toward a new font system, we expect to obsolete those soon: + // Clearing the atlas/fonts has little use nowadays, unless you want to batch remove all fonts. + // - Since 1.92, you can call ClearFonts() mid-frame, if you load new fonts afterwards. + // - As we are transitioning toward our new font system the semantic for those functions gets increasingly misleading and are often a source of issues. + // TL;DR; most likely, don't use any of those functions. We expect to obsolete/rework them. + IMGUI_API void Clear(); // Clear everything (fonts + textures). Don't call mid-frame! + IMGUI_API void ClearFonts(); // Clear input+output font data/glyphs. New fonts and textures will be recreated afterwards. IMGUI_API void ClearInputData(); // [OBSOLETE] Clear input data (all ImFontConfig structures including sizes, TTF data, glyph ranges, etc.) = all the data used to build the texture and fonts. IMGUI_API void ClearTexData(); // [OBSOLETE] Clear CPU-side copy of the texture data. Saves RAM once the texture has been copied to graphics memory. @@ -3885,7 +3935,7 @@ struct ImFont ImFontAtlas* OwnerAtlas; // 4-8 // What we have been loaded into. ImFontFlags Flags; // 4 // Font flags. float CurrentRasterizerDensity; // Current rasterizer density. This is a varying state of the font. - float LineHeight; // 4 // Line Height + float LineHeight; // 4 // [PCSX2 Custom] Line Height // [Internal] Members: Cold ~24-52 bytes // Conceptually Sources[] is the list of font sources merged to create this font. @@ -3917,7 +3967,7 @@ struct ImFont IMGUI_API void RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c, const ImVec4* cpu_fine_clip = NULL); IMGUI_API void RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, ImDrawTextFlags flags = 0); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - inline const char* CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) { return CalcWordWrapPosition(LegacySize * scale, text, text_end, wrap_width); } + inline const char* CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) { return CalcWordWrapPosition(LegacySize * scale, text, text_end, wrap_width); } // Obsoleted old name in 1.92.0. Note how `scale` was to `size`. #endif // [Internal] Don't use! @@ -4019,7 +4069,11 @@ struct ImGuiPlatformIO // Optional: Platform locale // [Experimental] Configure decimal point e.g. '.' or ',' useful for some languages (e.g. German), generally pulled from *localeconv()->decimal_point - ImWchar Platform_LocaleDecimalPoint; // '.' + ImWchar Platform_LocaleDecimalPoint; // '.' + + // Optional: Platform time/date + // This is automatically filled on startup. Used to store a "last used date" in some .ini structures. Facilitate creating tools to clean up old/unused data. + int Platform_SessionDate; // Integer storing YYYYMMDD e.g. 20261231 corresponding to the beginning of application session. //------------------------------------------------------------------ // Input - Interface with Renderer Backend @@ -4075,6 +4129,8 @@ struct ImGuiPlatformImeData #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS namespace ImGui { + // OBSOLETED in 1.92.9 (from July 2026) + IMGUI_API void SetColorEditOptions(ImGuiColorEditFlags flags); // set current options for if you want to select a default format, picker type, etc. User will be able to change those settings, unless you pass the _NoOptions flag to your calls. // OBSOLETED in 1.92.0 (from June 2025) inline void PushFont(ImFont* font) { PushFont(font, font ? font->LegacySize : 0.0f); } IMGUI_API void SetWindowFontScale(float scale); // Set font scale factor for current window. Prefer using PushFont(NULL, style.FontSizeBase * factor) or use style.FontScaleMain to scale all windows. diff --git a/3rdparty/imgui/include/imgui_internal.h b/3rdparty/imgui/include/imgui_internal.h index d66b6a9dbf..d2a8595a1c 100644 --- a/3rdparty/imgui/include/imgui_internal.h +++ b/3rdparty/imgui/include/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 +// dear imgui, v1.92.9b // (internal structures/api) // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility. @@ -35,7 +35,7 @@ Index of this file: // [SECTION] ImGuiContext (main imgui context) // [SECTION] ImGuiWindowTempData, ImGuiWindow // [SECTION] Tab bar, Tab item support -// [SECTION] Table support +// [SECTION] Table support + internal API // [SECTION] ImGui internal API // [SECTION] ImFontLoader // [SECTION] ImFontAtlas internal API @@ -98,6 +98,7 @@ Index of this file: #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision #pragma clang diagnostic ignored "-Wmissing-noreturn" // warning: function 'xxx' could be declared with attribute 'noreturn' #pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated +#pragma clang diagnostic ignored "-Wreserved-identifier" // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access #pragma clang diagnostic ignored "-Wnontrivial-memaccess" // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type #elif defined(__GNUC__) @@ -138,6 +139,7 @@ Index of this file: struct ImBitVector; // Store 1-bit per value struct ImRect; // An axis-aligned rectangle (2 points) struct ImGuiTextIndex; // Maintain a line index for a text buffer. +struct ImGuiPackedDate; // A date in YYYYMMDD format packed into 16-bits // ImDrawList/ImFontAtlas struct ImDrawDataBuilder; // Helper to build a ImDrawData instance @@ -247,6 +249,7 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer #define IMGUI_DEBUG_LOG_POPUP(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_NAV(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventNav) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_SELECTION(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) +#define IMGUI_DEBUG_LOG_TABLE(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventTable) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_CLIPPER(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventClipper) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_IO(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_FONT(...) do { ImGuiContext* g2 = GImGui; if (g2 && g2->DebugLogFlags & ImGuiDebugLogFlags_EventFont) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) // Called from ImFontAtlas function which may operate without a context. @@ -321,6 +324,8 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer #define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xde01") #elif defined(__GNUC__) && defined(__arm__) && !defined(__thumb__) #define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xe7f001f0") +#elif defined(__GNUC__) && defined(__aarch64__) +#define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xd4200000") // GDB needs 'set $pc=($pc+4)' to skip this :( #else #define IM_DEBUG_BREAK() IM_ASSERT(0) // It is expected that you define IM_DEBUG_BREAK() into something that will break nicely in a debugger! #endif @@ -531,6 +536,7 @@ inline float ImFloor(float f) { return inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2(ImFloor(v.x), ImFloor(v.y)); } inline float ImTrunc64(float f) { return (float)(ImS64)(f); } inline float ImRound64(float f) { return (float)(ImS64)(f + 0.5f); } // FIXME: Positive values only. +inline float ImCeilFast(float f) { int i = (int)f; return (float)(i + (f > (float)i)); } // Consider using the the bit-hack version (search for "0x1p120f"). inline int ImModPositive(int a, int b) { return (a + b) % b; } inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; } inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); } @@ -688,6 +694,8 @@ struct ImSpan inline void set(T* data, int size) { Data = data; DataEnd = data + size; } inline void set(T* data, T* data_end) { Data = data; DataEnd = data_end; } + inline void clear() { Data = DataEnd = NULL; } + inline bool empty() const { return Data == DataEnd; } inline int size() const { return (int)(ptrdiff_t)(DataEnd - Data); } inline int size_in_bytes() const { return (int)(ptrdiff_t)(DataEnd - Data) * (int)sizeof(T); } inline T& operator[](int i) { T* p = Data + i; IM_ASSERT(p >= Data && p < DataEnd); return *p; } @@ -827,6 +835,22 @@ struct ImGuiTextIndex void append(const char* base, int old_size, int new_size); }; +// Helper: ImGuiPackedDate (sizeof() == 2) +// Store a date in a way that is efficient to read/write in text form. If we stored e.g. number of days since Epoch we'd need costlier back and forth. +// This is specifically designed to be able to prune old .ini data. +struct ImGuiPackedDate +{ + ImU16 Year : 7; // Year since 2000 // We can change to another offset e.g. 1970 but this is easier to watch in debugger. + ImU16 Month : 4; // Month (1-12) + ImU16 Day : 5; // Day (1-31) + + ImGuiPackedDate() { Year = Month = Day = 0; } + ImGuiPackedDate(int yyyymmdd) { Year = (ImU16)((yyyymmdd / 10000) - 2000); Month = (ImU16)((yyyymmdd / 100) % 100); Day = (ImU16)(yyyymmdd % 100); } // Pack + bool IsValid() { return (Year && Month && Day); } + int Unpack() const { return (Year && Month && Day) ? ((Year + 2000) * 10000) + (Month * 100) + Day : 0; } // Unpack + void SubtractMonths(int m) { while (m > 0) { Year -= Month == 1; Month = (Month == 1) ? 12 : Month - 1; m--; } } // FIXME-OPT: Stupid but enough for what we do with it. +}; + // Helper: ImGuiStorage IMGUI_API ImGuiStoragePair* ImLowerBound(ImGuiStoragePair* in_begin, ImGuiStoragePair* in_end, ImGuiID key); @@ -872,7 +896,7 @@ struct IMGUI_API ImDrawListSharedData float FontSize; // Current font size (used for for simplified AddText overload) float FontScale; // Current font scale (== FontSize / Font->FontSize) float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo() - float CircleSegmentMaxError; // Number of circle segments to use per pixel of radius for AddCircle() etc + float CircleTessellationMaxError; // Number of circle segments to use per pixel of radius for AddCircle() etc float InitialFringeScale; // Initial scale to apply to AA fringe ImDrawListFlags InitialFlags; // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards) ImVec4 ClipRectFullscreen; // Value for PushClipRectFullscreen() @@ -982,7 +1006,7 @@ enum ImGuiItemFlagsPrivate_ ImGuiItemFlags_HasSelectionUserData = 1 << 21, // false // Set by SetNextItemSelectionUserData() ImGuiItemFlags_IsMultiSelect = 1 << 22, // false // Set by SetNextItemSelectionUserData() - ImGuiItemFlags_Default_ = ImGuiItemFlags_AutoClosePopups, // Please don't change, use PushItemFlag() instead. + ImGuiItemFlags_Default_ = ImGuiItemFlags_AutoClosePopups | ImGuiItemFlags_LiveEditOnInputText, // Please don't change, use PushItemFlag() instead. // Obsolete //ImGuiItemFlags_SelectableDontClosePopup = !ImGuiItemFlags_AutoClosePopups, // Can't have a redirect as we inverted the behavior @@ -1185,7 +1209,7 @@ struct IMGUI_API ImGuiGroupData ImVec2 BackupCurrLineSize; float BackupCurrLineTextBaseOffset; ImGuiID BackupActiveIdIsAlive; - bool BackupActiveIdHasBeenEditedThisFrame; + bool BackupAnyIdHasBeenEditedThisFrame; bool BackupDeactivatedIdIsAlive; bool BackupHoveredIdIsAlive; bool BackupIsSameLine; @@ -1215,10 +1239,11 @@ struct IMGUI_API ImGuiMenuColumns struct IMGUI_API ImGuiInputTextDeactivatedState { ImGuiID ID; // widget id owning the text state (which just got deactivated) + int ElapseFrame; ImVector TextA; // text buffer ImGuiInputTextDeactivatedState() { memset((void*)this, 0, sizeof(*this)); } - void ClearFreeMemory() { ID = 0; TextA.clear(); } + void ClearFreeMemory() { ID = 0; ElapseFrame = 0; TextA.clear(); } }; // Forward declare imstb_textedit.h structure + make its main configuration define accessible @@ -1240,6 +1265,7 @@ struct IMGUI_API ImGuiInputTextState ImStbTexteditState* Stb; // State for stb_textedit.h ImGuiInputTextFlags Flags; // copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags_Password is set. ImGuiID ID; // widget id owning the text state + //int LastFrameActive; // Last used frame-count (unused) int TextLen; // UTF-8 length of the string in TextA (in bytes) const char* TextSrc; // == TextA.Data unless read-only, in which case == buf passed to InputText(). For _ReadOnly fields, pointer will be null outside the InputText() call. ImVector TextA; // main UTF8 buffer. TextA.Size is a buffer size! Should always be >= buf_size passed by user (and of course >= CurLenA + 1). @@ -1363,7 +1389,7 @@ enum ImGuiNextItemDataFlags_ struct ImGuiNextItemData { ImGuiNextItemDataFlags HasFlags; // Called HasFlags instead of Flags to avoid mistaking this - ImGuiItemFlags ItemFlags; // Currently only tested/used for ImGuiItemFlags_AllowOverlap and ImGuiItemFlags_HasSelectionUserData. + ImGuiItemFlags ItemFlagsSet; // Currently only tested/used for ImGuiItemFlags_AllowOverlap and ImGuiItemFlags_HasSelectionUserData. // Members below are NOT cleared by ItemAdd() meaning they are still valid during e.g. NavProcessItem(). Always rely on HasFlags. ImGuiID FocusScopeId; // Set by SetNextItemSelectionUserData() @@ -1378,7 +1404,7 @@ struct ImGuiNextItemData ImU32 ColorMarker; // Set by SetNextItemColorMarker(). Not exposed yet, supported by DragScalar,SliderScalar and for ImGuiSliderFlags_ColorMarkers. ImGuiNextItemData() { memset((void*)this, 0, sizeof(*this)); SelectionUserData = -1; } - inline void ClearFlags() { HasFlags = ImGuiNextItemDataFlags_None; ItemFlags = ImGuiItemFlags_None; } // Also cleared manually by ItemAdd()! + inline void ClearFlags() { HasFlags = ImGuiNextItemDataFlags_None; ItemFlagsSet = ImGuiItemFlags_None; } // Also cleared manually by ItemAdd()! }; // Status storage for the last submitted item @@ -1702,6 +1728,7 @@ enum ImGuiActivateFlags_ }; // Early work-in-progress API for ScrollToItem() +// FIXME: Missing flags to request making both edges visible when possible. enum ImGuiScrollFlags_ { ImGuiScrollFlags_None = 0, @@ -1721,8 +1748,8 @@ enum ImGuiNavRenderCursorFlags_ ImGuiNavRenderCursorFlags_None = 0, ImGuiNavRenderCursorFlags_Compact = 1 << 1, // Compact highlight, no padding/distance from focused item ImGuiNavRenderCursorFlags_AlwaysDraw = 1 << 2, // Draw rectangular highlight if (g.NavId == id) even when g.NavCursorVisible == false, aka even when using the mouse. - ImGuiNavRenderCursorFlags_NoRounding = 1 << 3, #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + ImGuiNavRenderCursorFlags_NoRounding = 1 << 3, ImGuiNavHighlightFlags_None = ImGuiNavRenderCursorFlags_None, // Renamed in 1.91.4 ImGuiNavHighlightFlags_Compact = ImGuiNavRenderCursorFlags_Compact, // Renamed in 1.91.4 ImGuiNavHighlightFlags_AlwaysDraw = ImGuiNavRenderCursorFlags_AlwaysDraw, // Renamed in 1.91.4 @@ -1933,6 +1960,7 @@ struct IMGUI_API ImGuiMultiSelectTempData bool NavIdPassedBy; bool RangeSrcPassedBy; // Set by the item that matches RangeSrcItem. bool RangeDstPassedBy; // Set by the item that matches NavJustMovedToId when IsSetRange is set. + bool IsSoleOrUnknownSelectionSize; ImGuiMultiSelectTempData() { Clear(); } void Clear() { size_t io_sz = sizeof(IO); ClearIO(); memset((void*)(&IO + 1), 0, sizeof(*this) - io_sz); } // Zero-clear except IO as we preserve IO.Requests[] buffer allocation. @@ -2007,18 +2035,30 @@ struct ImGuiViewportP : public ImGuiViewport // (this is designed to be stored in a ImChunkStream buffer, with the variable-length Name following our structure) struct ImGuiWindowSettings { - ImGuiID ID; - ImVec2ih Pos; - ImVec2ih Size; - bool Collapsed; - bool IsChild; - bool WantApply; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context) - bool WantDelete; // Set to invalidate/delete the settings entry + ImGuiID ID; + ImVec2ih Pos; + ImVec2ih Size; + ImGuiPackedDate LastUsedDate; + bool Collapsed : 1; + bool IsChild : 1; + bool WantApply : 1; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context) + bool WantDelete : 1; // Set to invalidate/delete the settings entry ImGuiWindowSettings() { memset((void*)this, 0, sizeof(*this)); } char* GetName() { return (char*)(this + 1); } }; +struct ImGuiSettingsCleanupArgs +{ + ImGuiID TypeHashFilter = 0; // Set to restrict cleanup to a given .ini type, e.g. == ImHashStr("Window"), ImHashStr("Table"). Otherwise every types supporting Cleanup will be affected. + int DiscardOlderThanMonths = 0; // Enable to discard entries older than XX months. + bool DiscardWhenMissingDate = false; // Enable to discard entries missing a date. + bool DiscardAll = false; // Enable to discard all entries = same as calling ClearIniSettings() except it may be filtered. + bool SetCurrentSessionDateToAll = false; // Enable to write current SessionDate to all supporting entries. // Let us know in #9460 if you use this. + bool SetCurrentSessionDateWhenMissingDate = false; // Enable to write current SessionDate to all supporting entries missing a date. // Let us know in #9460 if you use this. + int _DiscardOlderThanDate = 0; // [Internal] +}; + struct ImGuiSettingsHandler { const char* TypeName; // Short description stored in .ini file. Disallowed characters: '[' ']' @@ -2029,6 +2069,7 @@ struct ImGuiSettingsHandler void (*ReadLineFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line); // Read: Called for every line of text within an ini entry void (*ApplyAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler); // Read: Called after reading (in registration order) void (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf); // Write: Output every entries into 'out_buf' + void (*CleanupFn) (ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiSettingsCleanupArgs* args);// Cleanup/patch settings void* UserData; ImGuiSettingsHandler() { memset((void*)this, 0, sizeof(*this)); } @@ -2045,7 +2086,10 @@ enum ImGuiLocKey : int ImGuiLocKey_TableSizeOne, ImGuiLocKey_TableSizeAllFit, ImGuiLocKey_TableSizeAllDefault, + ImGuiLocKey_TableReset, + //ImGuiLocKey_TableResetAll, ImGuiLocKey_TableResetOrder, + ImGuiLocKey_TableResetVisibility, ImGuiLocKey_WindowingMainMenuBar, ImGuiLocKey_WindowingPopup, ImGuiLocKey_WindowingUntitled, @@ -2099,8 +2143,9 @@ enum ImGuiDebugLogFlags_ ImGuiDebugLogFlags_EventInputRouting = 1 << 9, ImGuiDebugLogFlags_EventDocking = 1 << 10, // Unused in this branch ImGuiDebugLogFlags_EventViewport = 1 << 11, // Unused in this branch + ImGuiDebugLogFlags_EventTable = 1 << 12, - ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO | ImGuiDebugLogFlags_EventFont | ImGuiDebugLogFlags_EventInputRouting | ImGuiDebugLogFlags_EventDocking | ImGuiDebugLogFlags_EventViewport, + ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventTable | ImGuiDebugLogFlags_EventIO | ImGuiDebugLogFlags_EventFont | ImGuiDebugLogFlags_EventInputRouting | ImGuiDebugLogFlags_EventDocking | ImGuiDebugLogFlags_EventViewport, ImGuiDebugLogFlags_OutputToTTY = 1 << 20, // Also send output to TTY ImGuiDebugLogFlags_OutputToDebugger = 1 << 21, // Also send output to Debugger Console [Windows only] ImGuiDebugLogFlags_OutputToTestEngine = 1 << 22, // Also send output to Dear ImGui Test Engine @@ -2138,6 +2183,8 @@ struct ImGuiMetricsConfig int ShowTablesRectsType = -1; int HighlightMonitorIdx = -1; ImGuiID HighlightViewportID = 0; + int SettingsDiscardMonths = 6; + bool SettingsHighlightOldEntries = false; bool ShowFontPreview = true; }; @@ -2266,10 +2313,13 @@ struct ImGuiContext bool HoveredIdAllowOverlap; bool HoveredIdIsDisabled; // At least one widget passed the rect test, but has been discarded by disabled flag or popup inhibit. May be true even if HoveredId == 0. bool ItemUnclipByLog; // Disable ItemAdd() clipping, essentially a memory-locality friendly copy of LogEnabled + bool AnyIdHasBeenEditedThisFrame; ImGuiID ActiveId; // Active widget ImGuiID ActiveIdIsAlive; // Active widget has been seen this frame (we can't use a bool as the ActiveId may change within the frame) float ActiveIdTimer; bool ActiveIdIsJustActivated; // Set at the time of activation for one frame + bool ActiveIdWasSelected; // Active ID was selected at the time of activating + bool ActiveIdWasSoleSelected; // Active ID was sole selection at the time of activating bool ActiveIdAllowOverlap; // Active widget allows another widget to steal active id (generally for overlapping widgets, but not always) bool ActiveIdNoClearOnFocusLoss; // Disable losing active id if the active id window gets unfocused. bool ActiveIdHasBeenPressedBefore; // Track whether the active id led to a press (this is to allow changing between PressOnClick and PressOnRelease without pressing twice). Used by range_select branch. @@ -2286,6 +2336,8 @@ struct ImGuiContext ImGuiDataTypeStorage ActiveIdValueOnActivation; // Backup of initial value at the time of activation. ONLY SET BY SPECIFIC WIDGETS: DragXXX and SliderXXX. ImGuiID LastActiveId; // Store the last non-zero ActiveId, useful for animation. float LastActiveIdTimer; // Store the last non-zero ActiveId timer since the beginning of activation, useful for animation. + bool LastActiveIdWasSelected; + bool LastActiveIdWasSoleSelected; // Key/Input Ownership + Shortcut Routing system // - The idea is that instead of "eating" a given key, we can link to an owner. @@ -2482,7 +2534,6 @@ struct ImGuiContext ImGuiDataTypeStorage DataTypeZeroValue; // 0 for all data types int BeginMenuDepth; int BeginComboDepth; - ImGuiColorEditFlags ColorEditOptions; // Store user options for color edit widgets ImGuiID ColorEditCurrentID; // Set temporarily while inside of the parent-most ColorEdit4/ColorPicker4 (because they call each others). ImGuiID ColorEditSavedID; // ID we are saving/restoring HS for float ColorEditSavedHue; // Backup of last Hue associated to LastColor, so we can restore Hue in lossy RGB<>HSV round trips @@ -2517,6 +2568,7 @@ struct ImGuiContext ImVector UserTextures; // List of textures created/managed by user or third-party extension. Automatically appended into platform_io.Textures[]. // Settings + ImGuiPackedDate SessionDate; // Packed copy of platform_io.Platform_SessionDate, when valid. bool SettingsLoaded; float SettingsDirtyTimer; // Save .ini Settings to memory when time reaches zero ImGuiTextBuffer SettingsIniData; // In memory .ini settings @@ -2876,13 +2928,13 @@ struct IMGUI_API ImGuiTabBar }; //----------------------------------------------------------------------------- -// [SECTION] Table support +// [SECTION] Table support + internal API //----------------------------------------------------------------------------- #define IM_COL32_DISABLE IM_COL32(0,0,0,1) // Special sentinel code which cannot be used as a regular color. #define IMGUI_TABLE_MAX_COLUMNS 512 // Arbitrary "safety" maximum, may be lifted in the future if needed. Must fit in ImGuiTableColumnIdx/ImGuiTableDrawChannelIdx. -// [Internal] sizeof() ~ 112 +// [Internal] sizeof() ~ 120 // We use the terminology "Enabled" to refer to a column that is not Hidden by user/api. // We use the terminology "Clipped" to refer to a column that is out of sight because of scrolling/clipping. // This is in contrast with some user-facing api such as IsItemVisible() / IsRectVisible() which use "Visible" to mean "not clipped". @@ -2898,7 +2950,8 @@ struct ImGuiTableColumn float StretchWeight; // Master width weight when (Flags & _WidthStretch). Often around ~1.0f initially. float InitStretchWeightOrWidth; // Value passed to TableSetupColumn(). For Width it is a content width (_without padding_). ImRect ClipRect; // Clipping rectangle for the column - ImGuiID UserID; // Optional, value passed to TableSetupColumn() + ImGuiID ID; // Hash of column name (ignoring top of ID stack), used for .ini persistence when available. + ImGuiID UserData; // (Optional) User data value passed to TableSetupColumn() float WorkMinX; // Contents region min ~(MinX + CellPaddingX + CellSpacingX1) == cursor start position when entering column float WorkMaxX; // Contents region max ~(MaxX - CellPaddingX - CellSpacingX2) float ItemWidth; // Current item width for the column, preserved across rows @@ -2922,10 +2975,14 @@ struct ImGuiTableColumn bool IsVisibleY; bool IsRequestOutput; // Return value for TableSetColumnIndex() / TableNextColumn(): whether we request user to output contents or not. bool IsSkipItems; // Do we want item submissions to this column to be completely ignored (no layout will happen). - bool IsPreserveWidthAuto; + bool IsPreserveWidthAuto : 1; + bool IsJustCreated : 1; + bool IsLoadedSettings : 1; + bool IsNeedReconcileSrc : 1; + bool IsNeedReconcileDst : 1; ImS8 NavLayerCurrent; // ImGuiNavLayer in 1 byte - ImU8 AutoFitQueue; // Queue of 8 values for the next 8 frames to request auto-fit - ImU8 CannotSkipItemsQueue; // Queue of 8 values for the next 8 frames to disable Clipped/SkipItem + ImU8 AutoFitQueue : 4; // Queue of 4 values for the next 4 frames to request auto-fit + ImU8 CannotSkipItemsQueue : 4; // Queue of 4 values for the next 4 frames to disable Clipped/SkipItem ImU8 SortDirection : 2; // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending ImU8 SortDirectionsAvailCount : 2; // Number of available sort directions (0 to 3) ImU8 SortDirectionsAvailMask : 4; // Mask of available sort directions (1-bit each) @@ -2940,10 +2997,28 @@ struct ImGuiTableColumn PrevEnabledColumn = NextEnabledColumn = -1; SortOrder = -1; SortDirection = ImGuiSortDirection_None; + IsJustCreated = true; DrawChannelCurrent = DrawChannelFrozen = DrawChannelUnfrozen = (ImU8)-1; } }; +// Passed to TableSetupColumn() +// sizeof() ~ 24+120 bytes +struct ImGuiTableReconcileColumnData +{ + // Setup data + ImGuiID ID; + ImS16 NameOffset; + ImGuiTableColumnFlags Flags; + float InitWidthOrWeight; + ImGuiID UserData; + + // Reconcile data + ImGuiTableColumnIdx ColumnNewIdx; // Index in the current table. + ImGuiTableColumnIdx ColumnOldIdx; // Index in the previous frame table. + ImGuiTableColumn ColumnOldData; // Full backup of the column. Could be avoided by storing 1 of them and applying reconcile in the right order. Not worth bothering. +}; + // Transient cell data stored per row. // sizeof() ~ 6 bytes struct ImGuiTableCellData @@ -3077,6 +3152,7 @@ struct IMGUI_API ImGuiTable bool IsLayoutLocked; // Set by TableUpdateLayout() which is called when beginning the first row. bool IsInsideRow; // Set when inside TableBeginRow()/TableEndRow(). bool IsInitializing; + bool IsReconcileMode; bool IsSortSpecsDirty; bool IsUsingHeaders; // Set when the first row had the ImGuiTableRowFlags_Headers flag. bool IsContextPopupOpen; // Set when default context menu is open (also see: ContextPopupColumn, InstanceInteracted). @@ -3084,8 +3160,10 @@ struct IMGUI_API ImGuiTable bool IsSettingsRequestLoad; bool IsSettingsDirty; // Set when table settings have changed and needs to be reported into ImGuiTableSettings data. bool IsDefaultDisplayOrder; // Set when display order is unchanged from default (DisplayOrder contains 0...Count-1) - bool IsResetAllRequest; + bool IsDefaultVisibility; // Set when enabled/visibility is unchanged from default + bool IsResetAllRequest; // Set to queue a call to TableResetSettings() in BeginTable() bool IsResetDisplayOrderRequest; + bool IsResetVisibilityRequest; bool IsUnfrozenRows; // Set when we got past the frozen row. bool IsDefaultSizingPolicy; // Set if user didn't explicitly set a sizing policy in BeginTable() bool IsActiveIdAliveBeforeTable; @@ -3103,14 +3181,19 @@ struct IMGUI_API ImGuiTable // - Accessing those requires chasing an extra pointer so for very frequently used data we leave them in the main table structure. // - We also leave out of this structure data that tend to be particularly useful for debugging/metrics. // FIXME-TABLE: more transient data could be stored in a stacked ImGuiTableTempData: e.g. SortSpecs. -// sizeof() ~ 136 bytes. +// sizeof() ~ 176 bytes. struct IMGUI_API ImGuiTableTempData { ImGuiID WindowID; // Shortcut to g.Tables[TableIndex]->OuterWindow->ID. int TableIndex; // Index in g.Tables.Buf[] pool float LastTimeActive; // Last timestamp this structure was used float AngledHeadersExtraWidth; // Used in EndTable() - ImVector AngledHeadersRequests; // Used in TableAngledHeadersRow() + ImVector AngledHeadersRequests; // Used in TableAngledHeadersRow() // FIXME: Single instance is enough? + + // Topology change + ImVector ReconcileColumnsRequests; // Used in TableSetupColumn(), TableUpdateLayout(). Cleared every frame. + void* OldColumnsRawData; // Used in BeginTable() -> TableUpdateLayout() when resizing. + ImSpan OldColumnsData; ImVec2 UserOuterSize; // outer_size.x passed to BeginTable() ImDrawListSplitter DrawSplitter; @@ -3131,23 +3214,25 @@ struct IMGUI_API ImGuiTableTempData struct ImGuiTableColumnSettings { float WidthOrWeight; - ImGuiID UserID; + ImGuiID ID; ImGuiTableColumnIdx Index; ImGuiTableColumnIdx DisplayOrder; ImGuiTableColumnIdx SortOrder; ImU8 SortDirection : 2; - ImS8 IsEnabled : 2; // "Visible" in ini file + ImS8 IsEnabled : 2; // "Visible" in .ini file ImU8 IsStretch : 1; + bool IsLoaded : 1; // Using during loading to mark finding a matching column. ImGuiTableColumnSettings() { WidthOrWeight = 0.0f; - UserID = 0; + ID = 0; Index = -1; DisplayOrder = SortOrder = -1; SortDirection = ImGuiSortDirection_None; IsEnabled = -1; IsStretch = 0; + IsLoaded = false; } }; @@ -3159,12 +3244,94 @@ struct ImGuiTableSettings float RefScale; // Reference scale to be able to rescale columns on font/dpi changes. ImGuiTableColumnIdx ColumnsCount; ImGuiTableColumnIdx ColumnsCountMax; // Maximum number of columns this settings instance can store, we can recycle a settings instance with lower number of columns but not higher - bool WantApply; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context) + ImGuiPackedDate LastUsedDate; + bool WantApply : 1; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context) ImGuiTableSettings() { memset((void*)this, 0, sizeof(*this)); } ImGuiTableColumnSettings* GetColumnSettings() { return (ImGuiTableColumnSettings*)(this + 1); } }; +namespace ImGui +{ + // Tables: Candidates for public API + IMGUI_API void TableOpenContextMenu(int column_n = -1); + 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 TableGetHoveredRow(); // Retrieve *PREVIOUS FRAME* hovered row. This difference with TableGetHoveredColumn() is the reason why this is not public yet. + IMGUI_API float TableGetHeaderRowHeight(); + IMGUI_API float TableGetHeaderAngledMaxLabelWidth(); + IMGUI_API void TablePushBackgroundChannel(); + IMGUI_API void TablePopBackgroundChannel(); + IMGUI_API void TablePushColumnChannel(int column_n); + IMGUI_API void TablePopColumnChannel(); + IMGUI_API void TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label_width, const ImGuiTableHeaderData* data, int data_count); + + // Tables: Internals + inline ImGuiTable* GetCurrentTable() { ImGuiContext& g = *GImGui; return g.CurrentTable; } + IMGUI_API ImGuiTable* TableFindByID(ImGuiID id); + IMGUI_API bool BeginTableEx(const char* name, ImGuiID id, int columns_count, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0, 0), float inner_width = 0.0f); + IMGUI_API void TableBeginInitMemory(ImGuiTable* table, int columns_count); + IMGUI_API void TableApplyQueuedRequests(ImGuiTable* table); + IMGUI_API void TableSetupDrawChannels(ImGuiTable* table); + IMGUI_API void TableReconcileColumns(ImGuiTable* table); + IMGUI_API void TableUpdateLayout(ImGuiTable* table); + IMGUI_API void TableUpdateBorders(ImGuiTable* table); + IMGUI_API void TableUpdateColumnsWeightFromWidth(ImGuiTable* table); + IMGUI_API void TableApplyExternalUnclipRect(ImGuiTable* table, ImRect& rect); + IMGUI_API void TableDrawBorders(ImGuiTable* table); + IMGUI_API void TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags_for_section_to_display); + IMGUI_API bool TableBeginContextMenuPopup(ImGuiTable* table); + IMGUI_API void TableMergeDrawChannels(ImGuiTable* table); + inline ImGuiTableInstanceData* TableGetInstanceData(ImGuiTable* table, int instance_no) { if (instance_no == 0) return &table->InstanceDataFirst; return &table->InstanceDataExtra[instance_no - 1]; } + inline ImGuiID TableGetInstanceID(ImGuiTable* table, int instance_no) { return TableGetInstanceData(table, instance_no)->TableInstanceID; } + IMGUI_API void TableFixDisplayOrder(ImGuiTable* table); + IMGUI_API void TableSortSpecsSanitize(ImGuiTable* table); + IMGUI_API void TableSortSpecsBuild(ImGuiTable* table); + IMGUI_API void TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags init_mask); + IMGUI_API ImGuiSortDirection TableGetColumnNextSortDirection(ImGuiTableColumn* column); + IMGUI_API void TableFixColumnSortDirection(ImGuiTable* table, ImGuiTableColumn* column); + IMGUI_API float TableGetColumnWidthAuto(ImGuiTable* table, ImGuiTableColumn* column); + IMGUI_API void TableBeginRow(ImGuiTable* table); + IMGUI_API void TableEndRow(ImGuiTable* table); + IMGUI_API void TableBeginCell(ImGuiTable* table, int column_n); + IMGUI_API void TableEndCell(ImGuiTable* table); + IMGUI_API ImRect TableGetCellBgRect(const ImGuiTable* table, int column_n); + IMGUI_API const char* TableGetColumnName(const ImGuiTable* table, int column_n); + IMGUI_API ImGuiID TableGetColumnResizeID(ImGuiTable* table, int column_n, int instance_no = 0); + IMGUI_API float TableCalcMaxColumnWidth(const ImGuiTable* table, int column_n); + IMGUI_API void TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n); + IMGUI_API void TableSetColumnWidthAutoAll(ImGuiTable* table); + IMGUI_API void TableSetColumnDisplayOrder(ImGuiTable* table, int column_n, int dst_order); + IMGUI_API void TableQueueSetColumnDisplayOrder(ImGuiTable* table, int column_n, int dst_order); + IMGUI_API void TableRemove(ImGuiTable* table); + IMGUI_API void TableGcCompactTransientBuffers(ImGuiTable* table); + IMGUI_API void TableGcCompactTransientBuffers(ImGuiTableTempData* table); + IMGUI_API void TableGcCompactSettings(); + + // Tables: Settings + IMGUI_API void TableLoadSettings(ImGuiTable* table); + IMGUI_API void TableLoadSettingsForColumns(ImGuiTable* table); + IMGUI_API void TableLoadSettingsForColumn(ImGuiTableColumn* column, ImGuiTableColumnSettings* column_settings, ImGuiTableFlags load_flags); + IMGUI_API void TableSaveSettings(ImGuiTable* table); + IMGUI_API void TableResetSettings(ImGuiTable* table); + IMGUI_API ImGuiTableSettings* TableGetBoundSettings(ImGuiTable* table); + IMGUI_API void TableSettingsAddSettingsHandler(); + IMGUI_API ImGuiTableSettings* TableSettingsCreate(ImGuiID id, int columns_count); + IMGUI_API ImGuiTableSettings* TableSettingsFindByID(ImGuiID id); + + // Legacy Columns API (this is not exposed because we will encourage transitioning to the Tables API) + IMGUI_API void SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect); + IMGUI_API void BeginColumns(const char* str_id, int count, ImGuiOldColumnFlags flags = 0); // setup number of columns. use an identifier to distinguish multiple column sets. close with EndColumns(). + IMGUI_API void EndColumns(); // close columns + IMGUI_API void PushColumnClipRect(int column_index); + IMGUI_API void PushColumnsBackground(); + IMGUI_API void PopColumnsBackground(); + IMGUI_API ImGuiID GetColumnsID(const char* str_id, int count); + IMGUI_API ImGuiOldColumns* FindOrCreateColumns(ImGuiWindow* window, ImGuiID id); + IMGUI_API float GetColumnOffsetFromNorm(const ImGuiOldColumns* columns, float offset_norm); + IMGUI_API float GetColumnNormFromOffset(const ImGuiOldColumns* columns, float offset); +} + //----------------------------------------------------------------------------- // [SECTION] ImGui internal API // No guarantee of forward compatibility here! @@ -3262,6 +3429,7 @@ namespace ImGui IMGUI_API void MarkIniSettingsDirty(); IMGUI_API void MarkIniSettingsDirty(ImGuiWindow* window); IMGUI_API void ClearIniSettings(); + IMGUI_API void CleanupIniSettings(ImGuiSettingsCleanupArgs* args); // [BETA] Expected to turn into a public API. Please report if you are using this! IMGUI_API void AddSettingsHandler(const ImGuiSettingsHandler* handler); IMGUI_API void RemoveSettingsHandler(const char* type_name); IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name); @@ -3337,7 +3505,7 @@ namespace ImGui // Popups, Modals IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_window_flags); IMGUI_API bool BeginPopupMenuEx(ImGuiID id, const char* label, ImGuiWindowFlags extra_window_flags); - IMGUI_API void OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags = ImGuiPopupFlags_None); + IMGUI_API bool OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags = ImGuiPopupFlags_None); IMGUI_API void ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup); IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup); IMGUI_API void ClosePopupsExceptModals(); @@ -3512,86 +3680,12 @@ namespace ImGui // Multi-Select API IMGUI_API void MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags); - IMGUI_API void MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed); + IMGUI_API void MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed, ImGuiMultiSelectFlags extra_flags = 0); IMGUI_API void MultiSelectAddSetAll(ImGuiMultiSelectTempData* ms, bool selected); IMGUI_API void MultiSelectAddSetRange(ImGuiMultiSelectTempData* ms, bool selected, int range_dir, ImGuiSelectionUserData first_item, ImGuiSelectionUserData last_item); inline ImGuiBoxSelectState* GetBoxSelectState(ImGuiID id) { ImGuiContext& g = *GImGui; return (id != 0 && g.BoxSelectState.ID == id && g.BoxSelectState.IsActive) ? &g.BoxSelectState : NULL; } inline ImGuiMultiSelectState* GetMultiSelectState(ImGuiID id) { ImGuiContext& g = *GImGui; return g.MultiSelectStorage.GetByKey(id); } - // Internal Columns API (this is not exposed because we will encourage transitioning to the Tables API) - IMGUI_API void SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect); - IMGUI_API void BeginColumns(const char* str_id, int count, ImGuiOldColumnFlags flags = 0); // setup number of columns. use an identifier to distinguish multiple column sets. close with EndColumns(). - IMGUI_API void EndColumns(); // close columns - IMGUI_API void PushColumnClipRect(int column_index); - IMGUI_API void PushColumnsBackground(); - IMGUI_API void PopColumnsBackground(); - IMGUI_API ImGuiID GetColumnsID(const char* str_id, int count); - IMGUI_API ImGuiOldColumns* FindOrCreateColumns(ImGuiWindow* window, ImGuiID id); - IMGUI_API float GetColumnOffsetFromNorm(const ImGuiOldColumns* columns, float offset_norm); - IMGUI_API float GetColumnNormFromOffset(const ImGuiOldColumns* columns, float offset); - - // Tables: Candidates for public API - IMGUI_API void TableOpenContextMenu(int column_n = -1); - 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 TableGetHoveredRow(); // Retrieve *PREVIOUS FRAME* hovered row. This difference with TableGetHoveredColumn() is the reason why this is not public yet. - IMGUI_API float TableGetHeaderRowHeight(); - IMGUI_API float TableGetHeaderAngledMaxLabelWidth(); - IMGUI_API void TablePushBackgroundChannel(); - IMGUI_API void TablePopBackgroundChannel(); - IMGUI_API void TablePushColumnChannel(int column_n); - IMGUI_API void TablePopColumnChannel(); - IMGUI_API void TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label_width, const ImGuiTableHeaderData* data, int data_count); - - // Tables: Internals - inline ImGuiTable* GetCurrentTable() { ImGuiContext& g = *GImGui; return g.CurrentTable; } - IMGUI_API ImGuiTable* TableFindByID(ImGuiID id); - IMGUI_API bool BeginTableEx(const char* name, ImGuiID id, int columns_count, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0, 0), float inner_width = 0.0f); - IMGUI_API void TableBeginInitMemory(ImGuiTable* table, int columns_count); - IMGUI_API void TableBeginApplyRequests(ImGuiTable* table); - IMGUI_API void TableSetupDrawChannels(ImGuiTable* table); - IMGUI_API void TableUpdateLayout(ImGuiTable* table); - IMGUI_API void TableUpdateBorders(ImGuiTable* table); - IMGUI_API void TableUpdateColumnsWeightFromWidth(ImGuiTable* table); - IMGUI_API void TableApplyExternalUnclipRect(ImGuiTable* table, ImRect& rect); - IMGUI_API void TableDrawBorders(ImGuiTable* table); - IMGUI_API void TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags_for_section_to_display); - IMGUI_API bool TableBeginContextMenuPopup(ImGuiTable* table); - IMGUI_API void TableMergeDrawChannels(ImGuiTable* table); - inline ImGuiTableInstanceData* TableGetInstanceData(ImGuiTable* table, int instance_no) { if (instance_no == 0) return &table->InstanceDataFirst; return &table->InstanceDataExtra[instance_no - 1]; } - inline ImGuiID TableGetInstanceID(ImGuiTable* table, int instance_no) { return TableGetInstanceData(table, instance_no)->TableInstanceID; } - IMGUI_API void TableFixDisplayOrder(ImGuiTable* table); - IMGUI_API void TableSortSpecsSanitize(ImGuiTable* table); - IMGUI_API void TableSortSpecsBuild(ImGuiTable* table); - IMGUI_API ImGuiSortDirection TableGetColumnNextSortDirection(ImGuiTableColumn* column); - IMGUI_API void TableFixColumnSortDirection(ImGuiTable* table, ImGuiTableColumn* column); - IMGUI_API float TableGetColumnWidthAuto(ImGuiTable* table, ImGuiTableColumn* column); - IMGUI_API void TableBeginRow(ImGuiTable* table); - IMGUI_API void TableEndRow(ImGuiTable* table); - IMGUI_API void TableBeginCell(ImGuiTable* table, int column_n); - IMGUI_API void TableEndCell(ImGuiTable* table); - IMGUI_API ImRect TableGetCellBgRect(const ImGuiTable* table, int column_n); - IMGUI_API const char* TableGetColumnName(const ImGuiTable* table, int column_n); - IMGUI_API ImGuiID TableGetColumnResizeID(ImGuiTable* table, int column_n, int instance_no = 0); - IMGUI_API float TableCalcMaxColumnWidth(const ImGuiTable* table, int column_n); - IMGUI_API void TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n); - IMGUI_API void TableSetColumnWidthAutoAll(ImGuiTable* table); - IMGUI_API void TableSetColumnDisplayOrder(ImGuiTable* table, int column_n, int dst_order); - IMGUI_API void TableQueueSetColumnDisplayOrder(ImGuiTable* table, int column_n, int dst_order); - IMGUI_API void TableRemove(ImGuiTable* table); - IMGUI_API void TableGcCompactTransientBuffers(ImGuiTable* table); - IMGUI_API void TableGcCompactTransientBuffers(ImGuiTableTempData* table); - IMGUI_API void TableGcCompactSettings(); - - // Tables: Settings - IMGUI_API void TableLoadSettings(ImGuiTable* table); - IMGUI_API void TableSaveSettings(ImGuiTable* table); - IMGUI_API void TableResetSettings(ImGuiTable* table); - IMGUI_API ImGuiTableSettings* TableGetBoundSettings(ImGuiTable* table); - IMGUI_API void TableSettingsAddSettingsHandler(); - IMGUI_API ImGuiTableSettings* TableSettingsCreate(ImGuiID id, int columns_count); - IMGUI_API ImGuiTableSettings* TableSettingsFindByID(ImGuiID id); - // Tab Bars inline ImGuiTabBar* GetCurrentTabBar() { ImGuiContext& g = *GImGui; return g.CurrentTabBar; } IMGUI_API ImGuiTabBar* TabBarFindByID(ImGuiID id); @@ -3628,7 +3722,7 @@ namespace ImGui IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f); IMGUI_API void RenderColorComponentMarker(const ImRect& bb, ImU32 col, float rounding); IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0); - IMGUI_API void RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags = ImGuiNavRenderCursorFlags_None); // Navigation highlight + IMGUI_API void RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags = ImGuiNavRenderCursorFlags_None, float rounding = -1.0f); // Navigation highlight #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS inline void RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags = ImGuiNavRenderCursorFlags_None) { RenderNavCursor(bb, id, flags); } // Renamed in 1.91.4 #endif @@ -3707,7 +3801,7 @@ namespace ImGui IMGUI_API void InputTextDeactivateHook(ImGuiID id); IMGUI_API bool TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); IMGUI_API bool TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min = NULL, const void* p_clamp_max = NULL); - inline bool TempInputIsActive(ImGuiID id) { ImGuiContext& g = *GImGui; return g.ActiveId == id && g.TempInputId == id; } + inline bool TempInputIsActive(ImGuiID id) { ImGuiContext& g = *GImGui; return (g.TempInputId == id && g.ActiveId == id) || (g.InputTextDeactivatedState.ID == id); } inline ImGuiInputTextState* GetInputTextState(ImGuiID id) { ImGuiContext& g = *GImGui; return (id != 0 && g.InputTextState.ID == id) ? &g.InputTextState : NULL; } // Get input text state if active IMGUI_API void SetNextItemRefVal(ImGuiDataType data_type, void* p_data); inline bool IsItemActiveAsInputText() { ImGuiContext& g = *GImGui; return g.ActiveId != 0 && g.ActiveId == g.LastItemData.ID && g.InputTextState.ID == g.LastItemData.ID; } // This may be useful to apply workaround that a based on distinguish whenever an item is active as a text input field. @@ -3769,7 +3863,7 @@ namespace ImGui IMGUI_API void DebugNodeStorage(ImGuiStorage* storage, const char* label); IMGUI_API void DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label); IMGUI_API void DebugNodeTable(ImGuiTable* table); - IMGUI_API void DebugNodeTableSettings(ImGuiTableSettings* settings); + IMGUI_API void DebugNodeTableSettings(ImGuiTableSettings* settings, ImGuiTable* table); IMGUI_API void DebugNodeInputTextState(ImGuiInputTextState* state); IMGUI_API void DebugNodeTypingSelectState(ImGuiTypingSelectState* state); IMGUI_API void DebugNodeMultiSelectState(ImGuiMultiSelectState* state); @@ -3979,6 +4073,7 @@ IMGUI_API void ImFontAtlasTextureBlockFill(ImTextureData* dst_tex, IMGUI_API void ImFontAtlasTextureBlockCopy(ImTextureData* src_tex, int src_x, int src_y, ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h); IMGUI_API void ImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas, ImTextureData* tex, int x, int y, int w, int h); +IMGUI_API bool ImTextureDataUpdateNewFrame(ImTextureData* tex); IMGUI_API void ImTextureDataQueueUpload(ImTextureData* tex, int x, int y, int w, int h); IMGUI_API int ImTextureDataGetFormatBytesPerPixel(ImTextureFormat format); IMGUI_API const char* ImTextureDataGetStatusName(ImTextureStatus status); diff --git a/3rdparty/imgui/src/imstb_rectpack.h b/3rdparty/imgui/include/imstb_rectpack.h similarity index 100% rename from 3rdparty/imgui/src/imstb_rectpack.h rename to 3rdparty/imgui/include/imstb_rectpack.h diff --git a/3rdparty/imgui/src/imstb_truetype.h b/3rdparty/imgui/include/imstb_truetype.h similarity index 100% rename from 3rdparty/imgui/src/imstb_truetype.h rename to 3rdparty/imgui/include/imstb_truetype.h diff --git a/3rdparty/imgui/src/imgui.cpp b/3rdparty/imgui/src/imgui.cpp index 2edb22be21..7fa2d49e8d 100644 --- a/3rdparty/imgui/src/imgui.cpp +++ b/3rdparty/imgui/src/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 +// dear imgui, v1.92.9b // (main code and documentation) // Help: @@ -32,7 +32,7 @@ // See LICENSE.txt for copyright and licensing details (standard MIT License). // This library is free but needs your support to sustain development and maintenance. // Businesses: you can support continued development via B2B invoiced technical support, maintenance and sponsoring contracts. -// PLEASE reach out at omar AT dearimgui DOT com. See https://github.com/ocornut/imgui/wiki/Funding +// PLEASE reach out at omar AT discohello DOT com. See https://github.com/ocornut/imgui/wiki/Funding // Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine. // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library. @@ -395,6 +395,17 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2026/07/20 (1.92.9) - DragXXX, SliderXXX, InputScalar: with `ImGuiItemFlags_LiveEditOnInputScalar` now defaulting to being disabled: + inputting a value with the keyboard doesn't write intermediate values to backing variable. (#9476) + - Before: DragFloat() with user typing "123" --> write back 1, then 12, then 123. + - After: DragFloat() with user typing "123" --> write back 123 when validated/unfocused. + You can enable the flag back for a given scope if needed by specific widget/behavior. + - 2026/07/20 (1.92.9) - ImDrawData: marked ImDrawData::CmdListsCount as obsolete (technically was obsoleted in 1.89.8). + - Before: draw_data->CmdListsCount + - After: draw_data->CmdLists.Size + - 2026/07/08 (1.92.9) - Drag and Drop: commented out legacy name `ImGuiDragDropFlags_SourceAutoExpirePayload` which obsoleted in 1.90.9 (July 2024). Use `ImGuiDragDropFlags_PayloadAutoExpire`. + - 2026/07/06 (1.92.9) - ColorEdit: obsoleted SetColorEditOptions() function added in 1.51 (June 2017) in favor of directly poking to io.ConfigColorEditFlags. More consistent and easier to discover. + - 2026/06/02 (1.92.9) - TreeNode: commented out legacy name ImGuiTreeNodeFlags_SpanTextWidth which was obsoleted in 1.90.7 (May 2024). Use ImGuiTreeNodeFlags_SpanLabelWidth instead. - 2026/05/07 (1.92.8) - DrawList: swapped the last two arguments of AddRect(), AddPolyline(), PathStroke(). - Before: void ImDrawList::AddRect(ImVec2 p_min, ImVec2 p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0, float thickness = 1.0f); - After: void ImDrawList::AddRect(ImVec2 p_min, ImVec2 p_max, ImU32 col, float rounding = 0.0f, float thickness = 1.0f, ImDrawFlags flags = 0); @@ -416,6 +427,8 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: The new order is also more convenient as `flags` are less frequently used than `thickness` in real code. - As a general policy in Dear ImGui, all our flags default to 0 so ImDrawFlags_None was likely written 0 in some call sites. - Consider adding `#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS` in your imconfig.h, even temporarily, to clean up legacy code. + - 2026/05/07 (1.92.8) - DrawList: changed value of `ImDrawFlags_Closed`. It was previously advertised as "always == 1" when introduced in 1.82 (2021/02), in order to facilitate backward compatibility with the legacy `bool closed` flag. + This guarantee has been removed. The bit is reserved and `AddPolyline()`, `PathStroke()` will assert when it is used. - 2026/04/23 (1.92.8) - DrawList: obsoleted `ImDrawCallback_ResetRenderState` in favor of using `ImGui::GetPlatformIO().DrawCallback_ResetRenderState`, which is part of our new standard draw callbacks. (#9378) - 2026/04/22 (1.92.8) - Backends: Vulkan: redesigned to use separate ImageView + Sampler instead of Combined Image Sampler. - When registering custom textures: changed ImGui_ImplVulkan_AddTexture() signature to remove Sampler. @@ -726,7 +739,9 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: - ImDrawCornerFlags_XXX -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details + grep commented names in sources. - commented out runtime support for hardcoded ~0 or 0x01..0x0F rounding flags values for AddRect()/AddRectFilled()/PathRect()/AddImageRounded() -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details - 2023/08/25 (1.89.9) - clipper: Renamed IncludeRangeByIndices() (also called ForceDisplayRangeByIndices() before 1.89.6) to IncludeItemsByIndex(). Kept inline redirection function. Sorry! - - 2023/07/12 (1.89.8) - ImDrawData: CmdLists now owned, changed from ImDrawList** to ImVector. Majority of users shouldn't be affected, but you cannot compare to NULL nor reassign manually anymore. Instead use AddDrawList(). (#6406, #4879, #1878) + - 2023/07/12 (1.89.8) - ImDrawData: CmdLists now owned, changed from ImDrawList** to ImVector. Majority of users shouldn't be affected, but you cannot compare to NULL nor reassign manually anymore. (#6406, #4879, #1878) + - use `draw_data->CmdLists.Size` instead of `draw_list->CmdListsCount`. + - use `draw_data->AddDrawList()` to add to a ImDrawData. - 2023/06/28 (1.89.7) - overlapping items: obsoleted 'SetItemAllowOverlap()' (called after item) in favor of calling 'SetNextItemAllowOverlap()' (called before item). 'SetItemAllowOverlap()' didn't and couldn't work reliably since 1.89 (2022-11-15). - 2023/06/28 (1.89.7) - overlapping items: renamed 'ImGuiTreeNodeFlags_AllowItemOverlap' to 'ImGuiTreeNodeFlags_AllowOverlap', 'ImGuiSelectableFlags_AllowItemOverlap' to 'ImGuiSelectableFlags_AllowOverlap'. Kept redirecting enums (will obsolete). - 2023/06/28 (1.89.7) - overlapping items: IsItemHovered() now by default return false when querying an item using AllowOverlap mode which is being overlapped. Use ImGuiHoveredFlags_AllowWhenOverlappedByItem to revert to old behavior. @@ -1183,6 +1198,7 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: Q: How can I use my own math types instead of ImVec2? Q: How can I interact with standard C++ types (such as std::string and std::vector)? Q: How can I display custom shapes? (using low-level ImDrawList API) + Q: About Multi-Threading >> See https://www.dearimgui.com/faq Q&A: Fonts, Text @@ -1208,7 +1224,7 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: ============== Q: How can I help? - A: - Businesses: please reach out to "omar AT dearimgui DOT com" if you work in a place using Dear ImGui! + A: - Businesses: please reach out to "omar AT discohello DOT com" if you work in a place using Dear ImGui! We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts. This is among the most useful thing you can do for Dear ImGui. With increased funding, we sustain and grow work on this project. >>> See https://github.com/ocornut/imgui/wiki/Funding @@ -1240,6 +1256,12 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: // System includes #include // vsnprintf, sscanf, printf #include // intptr_t +#ifndef IMGUI_DISABLE_TIME_FUNCTIONS +#include // time(), localtime_r()/localtime_s() +#if defined(_WIN32) +static tm* localtime_r(const time_t* timep, tm* result) { return localtime_s(result, timep) == 0 ? result : NULL; } +#endif +#endif // [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled #if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) @@ -1356,6 +1378,7 @@ static void AddWindowToSortBuffer(ImVector* out_sorted // Settings static void WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*); +static void WindowSettingsHandler_Cleanup(ImGuiContext*, ImGuiSettingsHandler*, ImGuiSettingsCleanupArgs* args); static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line); static void WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*); @@ -1516,7 +1539,7 @@ ImGuiStyle::ImGuiStyle() GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero. ImageRounding = 0.0f; // Rounding of Image() calls. - ImageBorderSize = 0.0f; // Thickness of border around tabs. + ImageBorderSize = 0.0f; // Thickness of border around Image() calls. TabRounding = 5.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. TabBorderSize = 0.0f; // Thickness of border around tabs. TabMinWidthBase = 1.0f; // Minimum tab width, to make tabs larger than their contents. TabBar buttons are not affected. @@ -1530,6 +1553,8 @@ ImGuiStyle::ImGuiStyle() TreeLinesFlags = ImGuiTreeNodeFlags_DrawLinesNone; TreeLinesSize = 1.0f; // Thickness of outlines when using ImGuiTreeNodeFlags_DrawLines. TreeLinesRounding = 0.0f; // Radius of lines connecting child nodes to the vertical line. + MenuItemRounding = 0.0f; // Radius of MenuItem, BeginMenu rounding. + SelectableRounding = 0.0f; // Radius of selectable rounding. MODIFYING THIS IS DISCOURAGED. CONTIGUOUS SELECTIONS WILL NOT LOOK RIGHT. (#7589) DragDropTargetRounding = 0.0f; // Radius of the drag and drop target frame. DragDropTargetBorderSize = 2.0f; // Thickness of the drag and drop target border. DragDropTargetPadding = 3.0f; // Size to expand the drag and drop target from actual target item size. @@ -1537,6 +1562,7 @@ ImGuiStyle::ImGuiStyle() ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. + InputTextCursorSize = 1.0f; // Thickness of cursor/caret in InputText(). SeparatorSize = 1.0f; // Thickness of border in Separator(). SeparatorTextBorderSize = 3.0f; // Thickness of border in SeparatorText(). SeparatorTextAlign = ImVec2(0.0f,0.5f);// Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center). @@ -1608,10 +1634,13 @@ void ImGuiStyle::ScaleAllSizes(float scale_factor) TabBarOverlineSize = ImTrunc(TabBarOverlineSize * scale_factor); TreeLinesSize = ImTrunc(TreeLinesSize * scale_factor); TreeLinesRounding = ImTrunc(TreeLinesRounding * scale_factor); + MenuItemRounding = ImTrunc(MenuItemRounding * scale_factor); + SelectableRounding = ImTrunc(SelectableRounding * scale_factor); DragDropTargetRounding = ImTrunc(DragDropTargetRounding * scale_factor); DragDropTargetBorderSize = ImTrunc(DragDropTargetBorderSize * scale_factor); DragDropTargetPadding = ImTrunc(DragDropTargetPadding * scale_factor); ColorMarkerSize = ImTrunc(ColorMarkerSize * scale_factor); + InputTextCursorSize = ImTrunc(InputTextCursorSize * scale_factor); SeparatorSize = ImTrunc(SeparatorSize * scale_factor); SeparatorTextBorderSize = ImTrunc(SeparatorTextBorderSize * scale_factor); SeparatorTextPadding = ImTrunc(SeparatorTextPadding * scale_factor); @@ -1653,8 +1682,8 @@ ImGuiIO::ImGuiIO() ConfigNavCursorVisibleAuto = true; ConfigNavCursorVisibleAlways = false; - // Miscellaneous options - MouseDrawCursor = false; + // Widget options + #ifdef __APPLE__ ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag #else @@ -1664,11 +1693,21 @@ ImGuiIO::ImGuiIO() ConfigInputTextCursorBlink = true; ConfigInputTextEnterKeepActive = false; ConfigDragClickToInputText = false; + ConfigColorEditFlags = ImGuiColorEditFlags_DefaultOptions_; // Current settings for ColorEdit/ColorPicker widgets. May be further edited by users, unless you also set ImGuiColorEditFlags_NoOptions. ConfigWindowsResizeFromEdges = true; ConfigWindowsMoveFromTitleBarOnly = false; ConfigWindowsCopyContentsWithCtrlC = false; ConfigScrollbarScrollByPage = true; + + // Ini Settings options + ConfigIniSettingsSaveLastUsedDate = true; + ConfigIniSettingsAutoDiscardMonths = 0; + ConfigDebugIniSettings = false; + + // Miscellaneous options + MouseDrawCursor = false; ConfigMemoryCompactTimer = 60.0f; + ConfigDebugIsDebuggerPresent = false; ConfigDebugHighlightIdConflicts = true; ConfigDebugHighlightIdConflictsShowItemPicker = true; @@ -1683,6 +1722,7 @@ ImGuiIO::ImGuiIO() // Inputs Behaviors MouseDoubleClickTime = 0.30f; MouseDoubleClickMaxDist = 6.0f; + MouseSingleClickDelay = 0.50f; MouseDragThreshold = 6.0f; KeyRepeatDelay = 0.275f; KeyRepeatRate = 0.050f; @@ -2820,7 +2860,7 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b) { - float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f; + float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.0f; int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t); int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t); int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t); @@ -2851,20 +2891,20 @@ ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in) // Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v) { - float K = 0.f; + float K = 0.0f; if (g < b) { ImSwap(g, b); - K = -1.f; + K = -1.0f; } if (r < g) { ImSwap(r, g); - K = -2.f / 6.f - K; + K = -2.0f / 6.0f - K; } const float chroma = r - (g < b ? g : b); - out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f)); + out_h = ImFabs(K + (g - b) / (6.0f * chroma + 1e-20f)); out_s = chroma / (r + 1e-20f); out_v = r; } @@ -3680,6 +3720,8 @@ static const ImGuiStyleVarInfo GStyleVarsInfo[] = { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersTextAlign)},// ImGuiStyleVar_TableAngledHeadersTextAlign { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TreeLinesSize)}, // ImGuiStyleVar_TreeLinesSize { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TreeLinesRounding)}, // ImGuiStyleVar_TreeLinesRounding + { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, MenuItemRounding)}, // ImGuiStyleVar_MenuItemRounding + { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SelectableRounding)}, // ImGuiStyleVar_SelectableRounding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, DragDropTargetRounding)}, // ImGuiStyleVar_DragDropTargetRounding { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign @@ -4021,7 +4063,7 @@ void ImGui::RenderColorComponentMarker(const ImRect& bb, ImU32 col, float roundi RenderRectFilledInRangeH(window->DrawList, bb, col, bb.Min.x, ImMin(bb.Min.x + g.Style.ColorMarkerSize, bb.Max.x), rounding); } -void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags) +void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags, float rounding) { ImGuiContext& g = *GImGui; if (id != g.NavId) @@ -4037,17 +4079,24 @@ void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFl if (window->DC.NavHideHighlightOneFrame) return; - float rounding = (flags & ImGuiNavRenderCursorFlags_NoRounding) ? 0.0f : g.Style.FrameRounding; +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + if (rounding < 0.0f && (flags & ImGuiNavRenderCursorFlags_NoRounding)) + rounding = 0.0f; +#endif + if (rounding < 0.0f) + rounding = g.Style.FrameRounding; + ImRect display_rect = bb; display_rect.ClipWith(window->ClipRect); - const float thickness = 2.0f; + const float scale_factor = GetScale(); // FIXME-DPI + const float thickness = (float)(int)ImMax(2.0f, 1.5f * scale_factor); if (flags & ImGuiNavRenderCursorFlags_Compact) { window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, thickness); } else { - const float distance = 3.0f + thickness * 0.5f; + const float distance = (float)(int)(3.0f + thickness * 0.5f); display_rect.Expand(ImVec2(distance, distance)); bool fully_visible = window->ClipRect.Contains(display_rect); if (!fully_visible) @@ -4156,7 +4205,10 @@ static const ImGuiLocEntry GLocalizationEntriesEnUS[] = { ImGuiLocKey_TableSizeOne, "Size column to fit###SizeOne" }, { ImGuiLocKey_TableSizeAllFit, "Size all columns to fit###SizeAll" }, { ImGuiLocKey_TableSizeAllDefault, "Size all columns to default###SizeAll" }, + { ImGuiLocKey_TableReset, "Reset" }, + //{ ImGuiLocKey_TableResetAll, "Reset to default###ResetAll" }, { ImGuiLocKey_TableResetOrder, "Reset order###ResetOrder" }, + { ImGuiLocKey_TableResetVisibility, "Reset visibility###ResetVisibility" }, { ImGuiLocKey_WindowingMainMenuBar, "(Main menu bar)" }, { ImGuiLocKey_WindowingPopup, "(Popup)" }, { ImGuiLocKey_WindowingUntitled, "(Untitled)" }, @@ -4209,10 +4261,12 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) HoveredIdIsDisabled = false; HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f; ItemUnclipByLog = false; + AnyIdHasBeenEditedThisFrame = false; ActiveId = 0; ActiveIdIsAlive = 0; ActiveIdTimer = 0.0f; ActiveIdIsJustActivated = false; + ActiveIdWasSelected = ActiveIdWasSoleSelected = false; ActiveIdAllowOverlap = false; ActiveIdNoClearOnFocusLoss = false; ActiveIdHasBeenPressedBefore = false; @@ -4229,6 +4283,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) memset(&ActiveIdValueOnActivation, 0, sizeof(ActiveIdValueOnActivation)); LastActiveId = 0; LastActiveIdTimer = 0.0f; + LastActiveIdWasSelected = LastActiveIdWasSoleSelected = false; LastKeyboardKeyPressTime = LastKeyModsChangeTime = LastKeyModsChangeFromNoneTime = -1.0; @@ -4323,7 +4378,6 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) TempInputId = 0; memset(&DataTypeZeroValue, 0, sizeof(DataTypeZeroValue)); BeginMenuDepth = BeginComboDepth = 0; - ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_; ColorEditCurrentID = ColorEditSavedID = 0; ColorEditSavedHue = ColorEditSavedSat = 0.0f; ColorEditSavedColor = 0; @@ -4411,6 +4465,7 @@ void ImGui::Initialize() ini_handler.TypeName = "Window"; ini_handler.TypeHash = ImHashStr("Window"); ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll; + ini_handler.CleanupFn = WindowSettingsHandler_Cleanup; ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen; ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine; ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll; @@ -4428,6 +4483,14 @@ void ImGui::Initialize() g.PlatformIO.Platform_OpenInShellFn = Platform_OpenInShellFn_DefaultImpl; g.PlatformIO.Platform_SetImeDataFn = Platform_SetImeDataFn_DefaultImpl; + // Setup session starting date +#ifndef IMGUI_DISABLE_TIME_FUNCTIONS + const time_t session_time = time(NULL); + struct tm session_datetime = {}; + if (localtime_r(&session_time, &session_datetime)) + g.PlatformIO.Platform_SessionDate = (session_datetime.tm_year + 1900) * 10000 + (session_datetime.tm_mon + 1) * 100 + session_datetime.tm_mday; +#endif + // Create default viewport ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID; @@ -4471,10 +4534,14 @@ void ImGui::Shutdown() for (ImFontAtlas* atlas : g.FontAtlases) { UnregisterFontAtlas(atlas); - if (atlas->RefCount == 0) + if (atlas->OwnerContext == &g) { - atlas->Locked = false; - IM_DELETE(atlas); + IM_ASSERT(atlas->RefCount == 0 && "Destroying context owning a ImFontAtlas which is still used elsewhere!"); + if (atlas->RefCount == 0) + { + atlas->Locked = false; + IM_DELETE(atlas); + } } } g.DrawListSharedData.TempBuffer.clear(); @@ -4778,6 +4845,7 @@ void ImGui::MarkItemEdited(ImGuiID id) // ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need to fill the data. ImGuiContext& g = *GImGui; + //IM_ASSERT(g.LastItemData.ID == id); // Failing cases include: "widgets_inputtext_scrolling", "widgets_inputtext_multiline_status", "widgets_selectable_input" = case of e.g TempInputText() overlayed manually with different ID (#2718) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_EditedInternal; if (g.LastItemData.ItemFlags & ImGuiItemFlags_NoMarkEdited) return; @@ -4786,16 +4854,15 @@ void ImGui::MarkItemEdited(ImGuiID id) if (g.ActiveId == id || g.ActiveId == 0) { // FIXME: Can't we fully rely on LastItemData yet? - g.ActiveIdHasBeenEditedThisFrame = true; - g.ActiveIdHasBeenEditedBefore = true; - if (g.DeactivatedItemData.ID == id) - g.DeactivatedItemData.HasBeenEditedBefore = true; + g.AnyIdHasBeenEditedThisFrame = g.ActiveIdHasBeenEditedThisFrame = g.ActiveIdHasBeenEditedBefore = true; } + if (g.DeactivatedItemData.ID == id) + g.DeactivatedItemData.HasBeenEditedBefore = true; // We accept a MarkItemEdited() on drag and drop targets (see https://github.com/ocornut/imgui/issues/1875#issuecomment-978243343) // We accept 'ActiveIdPreviousFrame == id' for InputText() returning an edit after it has been taken ActiveId away (#4714) // FIXME: This assert is getting a bit meaningless over time. It helped detect some unusual use cases but eventually it is becoming an unnecessary restriction. - IM_ASSERT(g.DragDropActive || g.ActiveId == id || g.ActiveId == 0 || g.ActiveIdPreviousFrame == id || g.NavJustMovedToId || (g.CurrentMultiSelect != NULL && g.BoxSelectState.IsActive)); + IM_ASSERT(g.DragDropActive || g.ActiveId == id || g.ActiveId == 0 || g.DeactivatedItemData.ID == id || g.ActiveIdPreviousFrame == id || g.NavJustMovedToId || (g.CurrentMultiSelect != NULL && g.BoxSelectState.IsActive)); } bool ImGui::IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags) @@ -5357,14 +5424,15 @@ void ImGui::UpdateMouseMovingWindowEndFrame() // Click on empty space to focus window and start moving // (after we're done with all our widgets) - if (g.IO.MouseClicked[0]) + if (IsMouseClicked(0, ImGuiInputFlags_None, ImGuiKeyOwner_NoOwner)) { // Handle the edge case of a popup being closed while clicking in its empty space. // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more. ImGuiWindow* hovered_root = hovered_window ? hovered_window->RootWindow : NULL; const bool is_closed_popup = hovered_root && (hovered_root->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(hovered_root->PopupId, ImGuiPopupFlags_AnyPopupLevel); + const bool is_queued_focus_request = g.NavMoveSubmitted && (g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi); - if (hovered_window != NULL && !is_closed_popup) + if (hovered_window != NULL && !is_closed_popup && !is_queued_focus_request) { StartMouseMovingWindow(hovered_window); //-V595 @@ -5395,7 +5463,7 @@ void ImGui::UpdateMouseMovingWindowEndFrame() // With right mouse button we close popups without changing focus based on where the mouse is aimed // Instead, focus will be restored to the window under the bottom-most closed popup. // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger) - if (g.IO.MouseClicked[1] && g.HoveredId == 0) + if (g.HoveredId == 0 && IsMouseClicked(1, ImGuiInputFlags_None, ImGuiKeyOwner_NoOwner)) { // Find the top-most window between HoveredWindow and the top-most Modal Window. // This is where we can trim the popup stack. @@ -5539,6 +5607,7 @@ void ImGui::NewFrame() g.Time += g.IO.DeltaTime; g.FrameCount += 1; + g.SessionDate = ImGuiPackedDate(g.PlatformIO.Platform_SessionDate); g.TooltipOverrideCount = 0; g.WindowsActiveCount = 0; g.MenusIdSubmittedThisFrame.resize(0); @@ -5607,9 +5676,15 @@ void ImGui::NewFrame() // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore) if (g.ActiveId) g.ActiveIdTimer += g.IO.DeltaTime; + if (g.ActiveId && g.ActiveId == g.LastActiveId) + { + g.LastActiveIdWasSelected = g.ActiveIdWasSelected; + g.LastActiveIdWasSoleSelected = g.ActiveIdWasSoleSelected; + } g.LastActiveIdTimer += g.IO.DeltaTime; g.ActiveIdPreviousFrame = g.ActiveId; g.ActiveIdIsAlive = 0; + g.AnyIdHasBeenEditedThisFrame = false; g.ActiveIdHasBeenEditedThisFrame = false; g.ActiveIdIsJustActivated = false; if (g.TempInputId != 0 && g.ActiveId != g.TempInputId) @@ -5624,6 +5699,8 @@ void ImGui::NewFrame() if (g.DeactivatedItemData.ElapseFrame < g.FrameCount) g.DeactivatedItemData.ID = 0; g.DeactivatedItemData.IsAlive = false; + if (g.InputTextDeactivatedState.ElapseFrame < g.FrameCount) + g.InputTextDeactivatedState.ID = 0; // Record when we have been stationary as this state is preserved while over same item. // FIXME: The way this is expressed means user cannot alter HoverStationaryDelay during the frame to use varying values. @@ -5742,8 +5819,11 @@ void ImGui::NewFrame() if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time) TableGcCompactTransientBuffers(g.Tables.GetByIndex(i)); for (ImGuiTableTempData& table_temp_data : g.TablesTempData) + { + table_temp_data.ReconcileColumnsRequests.clear(); // Unusual: clear every frame because this is rarely used. if (table_temp_data.LastTimeActive >= 0.0f && table_temp_data.LastTimeActive < memory_compact_start_time) TableGcCompactTransientBuffers(&table_temp_data); + } if (g.GcCompactAll) GcCompactTransientMiscBuffers(); g.GcCompactAll = false; @@ -5875,7 +5955,7 @@ static void FlattenDrawDataIntoSingleLayer(ImDrawDataBuilder* builder) static void InitViewportDrawData(ImGuiViewportP* viewport) { - ImGuiIO& io = ImGui::GetIO(); + ImGuiContext& g = *GImGui; ImDrawData* draw_data = &viewport->DrawDataP; viewport->DrawDataBuilder.Layers[0] = &draw_data->CmdLists; @@ -5884,13 +5964,16 @@ static void InitViewportDrawData(ImGuiViewportP* viewport) viewport->DrawDataBuilder.Layers[1]->resize(0); draw_data->Valid = true; - draw_data->CmdListsCount = 0; + draw_data->FrameCount = g.FrameCount; draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0; draw_data->DisplayPos = viewport->Pos; draw_data->DisplaySize = viewport->Size; - draw_data->FramebufferScale = io.DisplayFramebufferScale; + draw_data->FramebufferScale = g.IO.DisplayFramebufferScale; draw_data->OwnerViewport = viewport; - draw_data->Textures = &ImGui::GetPlatformIO().Textures; + draw_data->Textures = &g.PlatformIO.Textures; +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + draw_data->CmdListsCount = 0; +#endif } // Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering. @@ -6158,10 +6241,11 @@ void ImGui::Render() // We call _PopUnusedDrawCmd() last thing, as RenderDimmedBackgrounds() rely on a valid command being there (especially in docking branch). ImDrawData* draw_data = &viewport->DrawDataP; - IM_ASSERT(draw_data->CmdLists.Size == draw_data->CmdListsCount); for (ImDrawList* draw_list : draw_data->CmdLists) draw_list->_PopUnusedDrawCmd(); - +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + draw_data->CmdListsCount = draw_data->CmdLists.Size; +#endif g.IO.MetricsRenderVertices += draw_data->TotalVtxCount; g.IO.MetricsRenderIndices += draw_data->TotalIdxCount; } @@ -6194,11 +6278,9 @@ ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_tex ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL); // Round - // FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out. - // FIXME: Investigate using ceilf or e.g. - // - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c - // - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html - text_size.x = IM_TRUNC(text_size.x + 0.99999f); + // (see 7b0bf230, 4622fa4b6, #791 for details about this.) + // FIXME: Add a way to disable this. + text_size.x = ImCeilFast(text_size.x); return text_size; } @@ -6291,6 +6373,7 @@ bool ImGui::IsItemDeactivated() return g.DeactivatedItemData.ID == g.LastItemData.ID && g.LastItemData.ID != 0 && g.DeactivatedItemData.ElapseFrame >= g.FrameCount; } +// Since 1.92.9: consider using NoLiveEdit flags if all you need to that values are not written bo backing variable while typing. bool ImGui::IsItemDeactivatedAfterEdit() { ImGuiContext& g = *GImGui; @@ -6317,11 +6400,12 @@ bool ImGui::IsItemToggledOpen() return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false; } -// Call after a Selectable() or TreeNode() involved in multi-selection. -// Useful if you need the per-item information before reaching EndMultiSelect(), e.g. for rendering purpose. -// This is only meant to be called inside a BeginMultiSelect()/EndMultiSelect() block. -// (Outside of multi-select, it would be misleading/ambiguous to report this signal, as widgets -// return e.g. a pressed event and user code is in charge of altering selection in ways we cannot predict.) +// Call after a Selectable() or TreeNode() items inside a BeginMultiSelect()/EndMultiSelect() scope. +// - Useful if you need the per-item information before reaching EndMultiSelect(), e.g. for rendering purpose. +// Outside of a multi-select block: +// - It would be misleading/ambiguous to report this signal, as widgets return e.g. a pressed event, +// and user code is in charge of altering selection in ways we cannot predict. +// Prefer using 'if (IsItemClicked() && !IsItemToggledOpen())' for a manual reimplementation of selection. bool ImGui::IsItemToggledSelection() { ImGuiContext& g = *GImGui; @@ -6368,7 +6452,7 @@ bool ImGui::IsItemEdited() void ImGui::SetNextItemAllowOverlap() { ImGuiContext& g = *GImGui; - g.NextItemData.ItemFlags |= ImGuiItemFlags_AllowOverlap; + g.NextItemData.ItemFlagsSet |= ImGuiItemFlags_AllowOverlap; } #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS @@ -6646,28 +6730,19 @@ ImGuiWindow* ImGui::FindWindowByName(const char* name) static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings) { - window->Pos = ImTrunc(ImVec2(settings->Pos.x, settings->Pos.y)); - if (settings->Size.x > 0 && settings->Size.y > 0) - window->Size = window->SizeFull = ImTrunc(ImVec2(settings->Size.x, settings->Size.y)); - window->Collapsed = settings->Collapsed; -} - -static void InitOrLoadWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings) -{ - // Initial window state with e.g. default/arbitrary window position - // Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window. - const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - window->Pos = main_viewport->Pos + ImVec2(60, 60); - window->Size = window->SizeFull = ImVec2(0, 0); - window->SetWindowPosAllowFlags = window->SetWindowSizeAllowFlags = window->SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; - if (settings != NULL) { + window->Pos = ImTrunc(ImVec2(settings->Pos.x, settings->Pos.y)); + if (settings->Size.x > 0 && settings->Size.y > 0) + { + window->Size = window->SizeFull = ImTrunc(ImVec2(settings->Size.x, settings->Size.y)); + window->AutoFitFramesX = window->AutoFitFramesY = 0; + } + window->Collapsed = settings->Collapsed; SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false); - ApplyWindowSettings(window, settings); } - window->DC.CursorStartPos = window->DC.CursorMaxPos = window->DC.IdealMaxPos = window->Pos; // So first call to CalcWindowContentSizes() doesn't return crazy values + window->DC.CursorStartPos = window->DC.CursorMaxPos = window->DC.IdealMaxPos = window->Pos; // So first call to CalcWindowContentSizes() doesn't return crazy values if ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0) { window->AutoFitFramesX = window->AutoFitFramesY = 2; @@ -6675,14 +6750,28 @@ static void InitOrLoadWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* s } else { - if (window->Size.x <= 0.0f) - window->AutoFitFramesX = 2; - if (window->Size.y <= 0.0f) - window->AutoFitFramesY = 2; + window->AutoFitFramesX = (window->Size.x <= 0.0f) ? 2 : 0; + window->AutoFitFramesY = (window->Size.y <= 0.0f) ? 2 : 0; window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0); } } +// Note that 'settings' may be NULL. +// Sets _Once, _Appearing, _FirstUseEver. _FirstUseEver will be cleared again if there are settings. +static void InitOrLoadWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings) +{ + // Initial window state with e.g. default/arbitrary window position + // Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window. + ImGuiContext& g = *GImGui; + const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + window->Pos = main_viewport->Pos + ImVec2(60, 60); + window->Size = window->SizeFull = ImVec2(0, 0); + window->SetWindowPosAllowFlags = window->SetWindowSizeAllowFlags = window->SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; + ApplyWindowSettings(window, settings); + if (settings != NULL) + settings->LastUsedDate = g.SessionDate; +} + static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) { // Create window the first time @@ -7657,10 +7746,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (flags & ImGuiWindowFlags_AlwaysAutoResize) { if (!window_size_x_set_by_api) - window->Size.x = window->SizeFull.x = 0.f; + window->Size.x = window->SizeFull.x = 0.0f; if (!window_size_y_set_by_api) - window->Size.y = window->SizeFull.y = 0.f; - window->ContentSize = window->ContentSizeIdeal = ImVec2(0.f, 0.f); + window->Size.y = window->SizeFull.y = 0.0f; + window->ContentSize = window->ContentSizeIdeal = ImVec2(0.0f, 0.0f); } } @@ -7706,7 +7795,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) ImRect title_bar_rect = window->TitleBarRect(); if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && g.ActiveId == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max)) if (g.IO.MouseClickedCount[0] == 2 && GetKeyOwner(ImGuiKey_MouseLeft) == ImGuiKeyOwner_NoOwner) + { window->WantCollapseToggle = true; + SetKeyOwner(ImGuiKey_MouseLeft, window->MoveId); // Claim input the same way ButtonBehavior() does. Prevent a same-frame move from triggering other items. (#9439) + } if (window->WantCollapseToggle) { window->Collapsed = !window->Collapsed; @@ -8975,6 +9067,8 @@ static void ImGui::UpdateTexturesNewFrame() IM_ASSERT(atlas->RendererHasTextures == has_textures); } } + for (ImTextureData* tex : g.UserTextures) + ImTextureDataUpdateNewFrame(tex); } // Build a single texture list @@ -9036,7 +9130,7 @@ ImFont* ImGui::GetDefaultFont() return g.IO.FontDefault ? g.IO.FontDefault : atlas->Fonts[0]; } -// EXPERIMENTAL. Use ImTextureDataQueueUpload() to queue updates. +// EXPERIMENTAL. Use ImTextureDataQueueUpload() to queue updates. Textures logic will be automatically be updated in NewFrame(). void ImGui::RegisterUserTexture(ImTextureData* tex) { ImGuiContext& g = *GImGui; @@ -9391,6 +9485,7 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE // - IsMouseReleased() // - IsMouseDoubleClicked() // - GetMouseClickedCount() +// - GetItemClickedCountWithSingleClickDelay() // - IsMouseHoveringRect() [Internal] // - IsMouseDragPastThreshold() [Internal] // - IsMouseDragging() @@ -9970,6 +10065,8 @@ bool ImGui::IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id) return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyReleased(MouseButtonToKey(button), owner_id) } + +// Prefer higher-level helper GetItemClickedCountWithSingleClickDelay() // Use if you absolutely need to distinguish single-click from double-click by introducing a delay. // Generally use with 'delay >= io.MouseDoubleClickTime' + combined with a 'io.MouseClickedLastCount == 1' test. // This is a very rarely used UI idiom, but some apps use this: e.g. MS Explorer single click on an icon to rename. @@ -9977,8 +10074,12 @@ bool ImGui::IsMouseReleasedWithDelay(ImGuiMouseButton button, float delay) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); + if (IsMouseDown(button)) + return false; + if (delay < 0.0f) + delay = g.IO.MouseSingleClickDelay; const float time_since_release = (float)(g.Time - g.IO.MouseReleasedTime[button]); - return !IsMouseDown(button) && (time_since_release - g.IO.DeltaTime < delay) && (time_since_release >= delay); + return (time_since_release - g.IO.DeltaTime < delay) && (time_since_release >= delay); } bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button) @@ -10002,6 +10103,62 @@ int ImGui::GetMouseClickedCount(ImGuiMouseButton button) return g.IO.MouseClickedCount[button]; } +// FIXME: This is close to what BeginDragDropSource() is doing, maybe rework. +static ImGuiID LastItemOverlayButtonForNullId(ImGuiMouseButton mouse_button) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.LastItemData.ID == 0); + ImGuiWindow* window = g.CurrentWindow; + ImGuiID id = window->GetIDFromRectangle(g.LastItemData.Rect); + if (g.IO.MouseClicked[mouse_button] && ImGui::ItemHoverable(g.LastItemData.Rect, id, g.LastItemData.ItemFlags)) + { + ImGui::SetActiveID(id, window); + ImGui::FocusWindow(window); + } + else if (g.ActiveId == id) + { + ImGui::KeepAliveID(id); + if (!g.IO.MouseDown[mouse_button]) + ImGui::ClearActiveID(); + } + return id; +} + +// [BETA] Building block for disambiguation between single-click and double-click. +// - Returns 1 on single-click but delayed by io.MouseSingleClickDelay (which is always > io.MouseDoubleClickTime) after mouse release. +// - Returns 2+ on double-click and subsequent repeated clicks. +// In order use that to replicate Windows Explorer's "click on label to rename after a delay", +// When the function returns 1 for a delayed single click, you can add with further tests: +// - If you want to test that the mouse position AT THE TIME of the click (before the delay): you can use the 'io.MouseClickedPos[mouse_button]' position. +// - If you want to test that the mouse position is still over the item at the end of delay: you can use '&& IsItemHovered()'. +// - If you want to test that the item was selected or the sole selection AT THE TIME of the click (before the delay): you can test for 'g.LastActiveIdWasSelected' or 'g.LastActiveIdWasSoleSelected'. +// e.g. +// int click_count = ImGui::GetItemClickedCountWithSingleClickDelay(mouse_button); +// if (click_count == 1 && ImGui::GetCurrentContext()->LastActiveIdWasSoleSelected) +// StartRename(); +// if (click_count == 2) +// Launch(); +int ImGui::GetItemClickedCountWithSingleClickDelay(ImGuiMouseButton mouse_button, float delay) +{ + // Action: double-click and subsequent clicks + ImGuiContext& g = *GImGui; + if (g.IO.MouseClickedCount[mouse_button] >= 2 && IsItemClicked(mouse_button)) + return g.IO.MouseClickedCount[mouse_button]; + + // Action: second click, delayed + ImGuiID id = g.LastItemData.ID; + if (id == 0) + id = LastItemOverlayButtonForNullId(mouse_button); + if (g.LastActiveId == id) + { + if (delay >= 0.0f) + delay = ImMax(delay, g.IO.MouseDoubleClickTime + 0.01f); + if (IsMouseReleasedWithDelay(mouse_button, delay) && g.IO.MouseClickedLastCount[mouse_button] == 1) + return 1; + } + return 0; +} + // Test if mouse cursor is hovering given rectangle // NB- Rectangle is clipped by our current clip setting // NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding) @@ -10948,7 +11105,13 @@ static void ImGui::ErrorCheckNewFrameSanityChecks() IM_ASSERT(g.Style.WindowBorderHoverPadding > 0.0f && "Invalid style setting!"); // Required otherwise cannot resize from borders. IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right); IM_ASSERT(g.Style.ColorButtonPosition == ImGuiDir_Left || g.Style.ColorButtonPosition == ImGuiDir_Right); + IM_ASSERT(ImIsPowerOfTwo(g.IO.ConfigColorEditFlags & ImGuiColorEditFlags_DisplayMask_)); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo(g.IO.ConfigColorEditFlags & ImGuiColorEditFlags_DataTypeMask_)); // " + IM_ASSERT(ImIsPowerOfTwo(g.IO.ConfigColorEditFlags & ImGuiColorEditFlags_PickerMask_)); // " + IM_ASSERT(ImIsPowerOfTwo(g.IO.ConfigColorEditFlags & ImGuiColorEditFlags_InputMask_)); // " + IM_ASSERT(g.Style.TreeLinesFlags == ImGuiTreeNodeFlags_DrawLinesNone || g.Style.TreeLinesFlags == ImGuiTreeNodeFlags_DrawLinesFull || g.Style.TreeLinesFlags == ImGuiTreeNodeFlags_DrawLinesToNodes); + IM_ASSERT(g.IO.MouseSingleClickDelay > g.IO.MouseDoubleClickTime); // Error handling: we do not accept 100% silent recovery! Please contact me if you feel this is getting in your way. if (g.IO.ConfigErrorRecovery) @@ -11299,7 +11462,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu g.LastItemData.ID = id; g.LastItemData.Rect = bb; g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb; - g.LastItemData.ItemFlags = g.CurrentItemFlags | g.NextItemData.ItemFlags | extra_flags; + g.LastItemData.ItemFlags = g.CurrentItemFlags | g.NextItemData.ItemFlagsSet | extra_flags; g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None; // Note: we don't copy 'g.NextItemData.SelectionUserData' to an hypothetical g.LastItemData.SelectionUserData: since the former is not cleared. @@ -11333,7 +11496,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu // Lightweight clear of SetNextItemXXX data. g.NextItemData.HasFlags = ImGuiNextItemDataFlags_None; - g.NextItemData.ItemFlags = ImGuiItemFlags_None; + g.NextItemData.ItemFlagsSet = ImGuiItemFlags_None; #ifdef IMGUI_ENABLE_TEST_ENGINE if (id != 0) @@ -11372,7 +11535,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu *p_highlight = g.FrameCount; *p_alive = g.FrameCount; if (*p_highlight >= g.FrameCount - 1) - window->DrawList->AddRect(bb.Min - ImVec2(1, 1), bb.Max + ImVec2(1, 1), IM_COL32(255, 0, 0, 255), 0.0f, ImDrawFlags_None, 2.0f); + window->DrawList->AddRect(bb.Min - ImVec2(1, 1), bb.Max + ImVec2(1, 1), IM_COL32(255, 0, 0, 255), 0.0f, 2.0f); } #endif } @@ -11743,7 +11906,7 @@ void ImGui::BeginGroup() group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive; group_data.BackupHoveredIdIsAlive = g.HoveredId != 0; group_data.BackupIsSameLine = window->DC.IsSameLine; - group_data.BackupActiveIdHasBeenEditedThisFrame = g.ActiveIdHasBeenEditedThisFrame; + group_data.BackupAnyIdHasBeenEditedThisFrame = g.AnyIdHasBeenEditedThisFrame; group_data.BackupDeactivatedIdIsAlive = g.DeactivatedItemData.IsAlive; group_data.EmitItem = true; @@ -11808,7 +11971,7 @@ void ImGui::EndGroup() g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; // Forward Edited flag - if (g.ActiveIdHasBeenEditedThisFrame && !group_data.BackupActiveIdHasBeenEditedThisFrame) + if (g.AnyIdHasBeenEditedThisFrame && !group_data.BackupAnyIdHasBeenEditedThisFrame) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited; // Forward Deactivated flag @@ -12270,24 +12433,26 @@ ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window) return NULL; } -void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags) +bool ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; ImGuiID id = g.CurrentWindow->GetID(str_id); IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopup(\"%s\" -> 0x%08X)\n", str_id, id); - OpenPopupEx(id, popup_flags); + return OpenPopupEx(id, popup_flags); } -void ImGui::OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags) +bool ImGui::OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags) { - OpenPopupEx(id, popup_flags); + return OpenPopupEx(id, popup_flags); } // Mark popup as open (toggle toward open state). -// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. -// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). -// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL) -void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags) +// - Return true when the popup is toggled open, which allows you to capture local state if needed. +// You may also call IsWindowAppearing() inside the later BeginPopup() scope if you need to prepare/compute data for the popup. +// - Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. +// - Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). +// - One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL). +bool ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* parent_window = g.CurrentWindow; @@ -12295,7 +12460,7 @@ void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags) if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup) if (IsPopupOpen((ImGuiID)0, ImGuiPopupFlags_AnyPopupId)) - return; + return false; ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack. popup_ref.PopupId = id; @@ -12310,6 +12475,7 @@ void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags) if (g.OpenPopupStack.Size < current_stack_size + 1) { g.OpenPopupStack.push_back(popup_ref); + return true; } else { @@ -12337,6 +12503,8 @@ void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags) // This is equivalent to what ClosePopupToLevel() does. //if (g.OpenPopupStack[current_stack_size].PopupId == id) // FocusWindow(parent_window); + + return !keep_existing; } } @@ -12621,16 +12789,17 @@ bool ImGui::IsPopupOpenRequestForWindow(ImGuiPopupFlags popup_flags) // Helper to open a popup if mouse button is released over the item // - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup() -void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags) +bool ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; if (IsPopupOpenRequestForItem(popup_flags, g.LastItemData.ID)) { + ImGuiWindow* window = g.CurrentWindow; ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) - OpenPopupEx(id, popup_flags); + return OpenPopupEx(id, popup_flags); } + return false; } // This is a helper to handle the simplest case of associating one named popup to one given widget. @@ -13797,13 +13966,13 @@ static void ImGui::NavUpdate() // FIXME-NAV: Now that keys are separated maybe we can get rid of NavInputSource? const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; const ImGuiKey nav_gamepad_keys_to_change_source[] = { ImGuiKey_GamepadFaceRight, ImGuiKey_GamepadFaceLeft, ImGuiKey_GamepadFaceUp, ImGuiKey_GamepadFaceDown, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown }; - if (nav_gamepad_active) + if (nav_gamepad_active && g.NavInputSource != ImGuiInputSource_Gamepad) for (ImGuiKey key : nav_gamepad_keys_to_change_source) if (IsKeyDown(key)) g.NavInputSource = ImGuiInputSource_Gamepad; const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; const ImGuiKey nav_keyboard_keys_to_change_source[] = { ImGuiKey_Space, ImGuiKey_Enter, ImGuiKey_Escape, ImGuiKey_RightArrow, ImGuiKey_LeftArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow }; - if (nav_keyboard_active) + if (nav_keyboard_active && g.NavInputSource != ImGuiInputSource_Keyboard) for (ImGuiKey key : nav_keyboard_keys_to_change_source) if (IsKeyDown(key)) g.NavInputSource = ImGuiInputSource_Keyboard; @@ -14343,7 +14512,7 @@ static void ImGui::NavUpdateContextMenuRequest() ImGuiContext& g = *GImGui; g.NavOpenContextMenuItemId = g.NavOpenContextMenuWindowId = 0; const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; - const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; + const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0; if ((!nav_keyboard_active && !nav_gamepad_active) || g.NavWindow == NULL) return; @@ -15569,6 +15738,26 @@ void ImGui::ClearIniSettings() handler.ClearAllFn(&g, &handler); } +void ImGui::CleanupIniSettings(ImGuiSettingsCleanupArgs* args) +{ + ImGuiContext& g = *GImGui; + if (args->DiscardAll) + for (ImGuiSettingsHandler& handler : g.SettingsHandlers) + if (args->TypeHashFilter == 0 || handler.TypeHash == args->TypeHashFilter) + if (handler.ClearAllFn != NULL) + handler.ClearAllFn(&g, &handler); + if (g.PlatformIO.Platform_SessionDate != 0 && args->DiscardOlderThanMonths != 0) + { + ImGuiPackedDate discard_older_than_date_p = g.PlatformIO.Platform_SessionDate; + discard_older_than_date_p.SubtractMonths(args->DiscardOlderThanMonths); + args->_DiscardOlderThanDate = discard_older_than_date_p.Unpack(); + } + for (ImGuiSettingsHandler& handler : g.SettingsHandlers) + if (args->TypeHashFilter == 0 || handler.TypeHash == args->TypeHashFilter) + if (handler.CleanupFn != NULL) + handler.CleanupFn(&g, &handler, args); +} + void ImGui::LoadIniSettingsFromDisk(const char* ini_filename) { size_t file_data_size = 0; @@ -15647,6 +15836,12 @@ void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) memcpy(buf, ini_data, ini_size); // Call post-read handlers + ImGuiSettingsCleanupArgs cleanup_args; + if (g.IO.ConfigIniSettingsAutoDiscardMonths > 0) + { + cleanup_args.DiscardOlderThanMonths = g.IO.ConfigIniSettingsAutoDiscardMonths; + CleanupIniSettings(&cleanup_args); + } for (ImGuiSettingsHandler& handler : g.SettingsHandlers) if (handler.ApplyAllFn != NULL) handler.ApplyAllFn(&g, &handler); @@ -15743,6 +15938,19 @@ static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandl g.SettingsWindows.clear(); } +static void WindowSettingsHandler_Cleanup(ImGuiContext* ctx, ImGuiSettingsHandler*, ImGuiSettingsCleanupArgs* args) +{ + ImGuiContext& g = *ctx; + for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) + { + const bool is_valid = settings->LastUsedDate.IsValid(); + if ((args->_DiscardOlderThanDate != 0 && settings->LastUsedDate.Unpack() < args->_DiscardOlderThanDate) || (args->DiscardWhenMissingDate && !is_valid)) + settings->WantDelete = true; + if (args->SetCurrentSessionDateToAll || (args->SetCurrentSessionDateWhenMissingDate && !is_valid)) + settings->LastUsedDate = g.SessionDate; + } +} + static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) { ImGuiID id = ImHashStr(name); @@ -15765,6 +15973,7 @@ static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) { settings->Size = ImVec2ih((short)x, (short)y); } else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); } else if (sscanf(line, "IsChild=%d", &i) == 1) { settings->IsChild = (i != 0); } + else if (sscanf(line, "LastUsed=%d", &i) == 1) { settings->LastUsedDate = i; return; } } // Apply to existing windows (if any) @@ -15794,6 +16003,7 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl if (!settings) { settings = ImGui::CreateNewWindowSettings(window->Name); + settings->LastUsedDate = g.SessionDate; window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings); } IM_ASSERT(settings->ID == window->ID); @@ -15802,6 +16012,7 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl settings->IsChild = (window->Flags & ImGuiWindowFlags_ChildWindow) != 0; settings->Collapsed = window->Collapsed; settings->WantDelete = false; + settings->LastUsedDate = g.SessionDate; } // Write to text buffer @@ -15811,7 +16022,7 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl if (settings->WantDelete) continue; const char* settings_name = settings->GetName(); - buf->appendf("[%s][%s]\n", handler->TypeName, settings_name); + buf->appendf("[%s][%s]\n", handler->TypeName, settings_name); // [Window][name] if (settings->IsChild) { buf->appendf("IsChild=1\n"); @@ -15824,6 +16035,9 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl if (settings->Collapsed) buf->appendf("Collapsed=1\n"); } + if (g.IO.ConfigIniSettingsSaveLastUsedDate) + if (int last_used_date = settings->LastUsedDate.Unpack()) + buf->appendf("LastUsed=%08d\n", last_used_date); buf->append("\n"); } } @@ -16596,6 +16810,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) { ImGuiContext& g = *GImGui; ImGuiIO& io = g.IO; + ImGuiPlatformIO& platform_io = g.PlatformIO; ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; if (cfg->ShowDebugLog) ShowDebugLogWindow(&cfg->ShowDebugLog); @@ -16928,8 +17143,36 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("\"%s\"", g.IO.IniFilename); else TextUnformatted(""); - Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings); Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer); + + int highlight_older_than_date = 0; + Text("SessionDate: %d", platform_io.Platform_SessionDate); + BeginDisabled(platform_io.Platform_SessionDate == 0); + Checkbox("Highlight Entries Older Than", &cfg->SettingsHighlightOldEntries); + SetNextItemWidth(GetFontSize() * 8); + SameLine(); + SliderInt("Months", &cfg->SettingsDiscardMonths, 1, 24); + if (cfg->SettingsHighlightOldEntries && cfg->SettingsDiscardMonths > 0) + { + ImGuiPackedDate cutoff_date = platform_io.Platform_SessionDate; + cutoff_date.SubtractMonths(cfg->SettingsDiscardMonths); + highlight_older_than_date = cutoff_date.Unpack(); + SameLine(); + ImGuiSettingsCleanupArgs cleanup_args; + cleanup_args.DiscardOlderThanMonths = cfg->SettingsDiscardMonths; + if (Button("Discard")) + CleanupIniSettings(&cleanup_args); + } + EndDisabled(); + Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings); + + struct ScopedHighlightOlderThan + { + bool Highlight; + ScopedHighlightOlderThan(int cutoff_date, ImGuiPackedDate in_date) { Highlight = cutoff_date != 0 && in_date.Unpack() < cutoff_date; if (Highlight) PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.4f, 0.4f, 1.0f)); } + ~ScopedHighlightOlderThan() { if (Highlight) PopStyleColor(); } + }; + if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size)) { for (ImGuiSettingsHandler& handler : g.SettingsHandlers) @@ -16939,14 +17182,20 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size())) { for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) + { + ScopedHighlightOlderThan scoped_highlight(highlight_older_than_date, settings->LastUsedDate); DebugNodeWindowSettings(settings); + } TreePop(); } if (TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size())) { for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) - DebugNodeTableSettings(settings); + { + ScopedHighlightOlderThan scoped_highlight(highlight_older_than_date, settings->LastUsedDate); + DebugNodeTableSettings(settings, NULL); + } TreePop(); } @@ -17761,7 +18010,7 @@ void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings* settings) { if (settings->WantDelete) BeginDisabled(); - Text("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d", + BulletText("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d", settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed); if (settings->WantDelete) EndDisabled(); @@ -17899,6 +18148,7 @@ void ImGui::ShowDebugLogWindow(bool* p_open) ShowDebugLogFlag("Nav", ImGuiDebugLogFlags_EventNav); ShowDebugLogFlag("Popup", ImGuiDebugLogFlags_EventPopup); ShowDebugLogFlag("Selection", ImGuiDebugLogFlags_EventSelection); + ShowDebugLogFlag("Table", ImGuiDebugLogFlags_EventTable); ShowDebugLogFlag("InputRouting", ImGuiDebugLogFlags_EventInputRouting); if (SmallButton("Clear")) @@ -18269,7 +18519,7 @@ void ImGui::ShowIDStackToolWindow(bool* p_open) Checkbox("Ctrl+C: copy path", &tool->OptCopyToClipboardOnCtrlC); PopStyleVar(); SameLine(); - TextColored((time_since_copy >= 0.0f && time_since_copy < 0.75f && ImFmod(time_since_copy, 0.25f) < 0.25f * 0.5f) ? ImVec4(1.f, 1.f, 0.3f, 1.f) : ImVec4(), "*COPIED*"); + TextColored((time_since_copy >= 0.0f && time_since_copy < 0.75f && ImFmod(time_since_copy, 0.25f) < 0.25f * 0.5f) ? ImVec4(1.0f, 1.0f, 0.3f, 1.0f) : ImVec4(), "*COPIED*"); if (tool->OptCopyToClipboardOnCtrlC && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused)) { tool->CopyToClipboardLastTime = (float)g.Time; diff --git a/3rdparty/imgui/src/imgui_demo.cpp b/3rdparty/imgui/src/imgui_demo.cpp index 7d882f803b..9f841b3c44 100644 --- a/3rdparty/imgui/src/imgui_demo.cpp +++ b/3rdparty/imgui/src/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 +// dear imgui, v1.92.9b // (demo code) // Help: @@ -86,6 +86,7 @@ Index of this file: // [SECTION] DemoWindowWidgetsFonts() // [SECTION] DemoWindowWidgetsImages() // [SECTION] DemoWindowWidgetsListBoxes() +// [SECTION] DemoWindowWidgetsLiveEdit() // [SECTION] DemoWindowWidgetsMultiComponents() // [SECTION] DemoWindowWidgetsPlotting() // [SECTION] DemoWindowWidgetsProgressBars() @@ -331,6 +332,8 @@ struct ImGuiDemoWindowData // Other data bool DisableSections = false; + bool LiveEditOverride = false; + ImGuiItemFlags LiveEditFlags = ImGuiItemFlags_LiveEditOnInputText; ExampleTreeNode* DemoTree = NULL; ~ImGuiDemoWindowData() { if (DemoTree) ExampleTree_DestroyNode(DemoTree); } @@ -538,6 +541,9 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::SameLine(); HelpMarker("Swap Cmd<>Ctrl keys, enable various MacOS style behaviors."); ImGui::Text("Also see Style->Rendering for rendering options."); + ImGui::SeparatorText("Settings"); + ImGui::Checkbox("io.ConfigIniSettingsSaveLastUsedDate", &io.ConfigIniSettingsSaveLastUsedDate); + // Also read: https://github.com/ocornut/imgui/wiki/Error-Handling ImGui::SeparatorText("Error Handling"); @@ -1323,6 +1329,7 @@ static void DemoWindowWidgetsColorAndPickers() ImGui::ColorEdit4("##RefColor", &ref_color_v.x, ImGuiColorEditFlags_NoInputs | base_flags); } } + ImGui::CheckboxFlags("ImGuiColorEditFlags_PickerNoRotate", &color_picker_flags, ImGuiColorEditFlags_PickerNoRotate); ImGui::Combo("Picker Mode", &picker_mode, "Auto/Current\0ImGuiColorEditFlags_PickerHueBar\0ImGuiColorEditFlags_PickerHueWheel\0"); ImGui::SameLine(); HelpMarker("When not specified explicitly, user can right-click the picker to change mode."); @@ -1331,7 +1338,7 @@ static void DemoWindowWidgetsColorAndPickers() ImGui::SameLine(); HelpMarker( "ColorEdit defaults to displaying RGB inputs if you don't specify a display mode, " "but the user can change it with a right-click on those inputs.\n\nColorPicker defaults to displaying RGB+HSV+Hex " - "if you don't specify a display mode.\n\nYou can change the defaults using SetColorEditOptions()."); + "if you don't specify a display mode.\n\nYou can change the defaults using io.ConfigColorEditFlags."); ImGuiColorEditFlags flags = base_flags | color_picker_flags; if (picker_mode == 1) flags |= ImGuiColorEditFlags_PickerHueBar; @@ -1344,14 +1351,14 @@ static void DemoWindowWidgetsColorAndPickers() ImGui::Text("Set defaults in code:"); ImGui::SameLine(); HelpMarker( - "SetColorEditOptions() is designed to allow you to set boot-time default.\n" + "io.ConfigColorEditFlags is designed to allow you to set boot-time default.\n" "We don't have Push/Pop functions because you can force options on a per-widget basis if needed, " "and the user can change non-forced ones with the options menu.\nWe don't have a getter to avoid " "encouraging you to persistently save values that aren't forward-compatible."); - if (ImGui::Button("Default: Uint8 + HSV + Hue Bar")) - ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_PickerHueBar); - if (ImGui::Button("Default: Float + HDR + Hue Wheel")) - ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_PickerHueWheel); + if (ImGui::Button("Overwrite default: Uint8 + HSV + Hue Bar")) + ImGui::GetIO().ConfigColorEditFlags = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_PickerHueBar; + if (ImGui::Button("Overwrite default: Float + HDR + Hue Wheel")) + ImGui::GetIO().ConfigColorEditFlags = ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_PickerHueWheel; // Always display a small version of both types of pickers // (that's in order to make it more visible in the demo to people who are skimming quickly through it) @@ -1740,7 +1747,7 @@ static void DemoWindowWidgetsDragAndDrop() if (ImGui::IsItemActive() && !ImGui::IsItemHovered()) { - int n_next = n + (ImGui::GetMouseDragDelta(0).y < 0.f ? -1 : 1); + int n_next = n + (ImGui::GetMouseDragDelta(0).y < 0.0f ? -1 : 1); if (n_next >= 0 && n_next < IM_COUNTOF(item_names)) { item_names[n] = item_names[n_next]; @@ -2005,6 +2012,44 @@ static void DemoWindowWidgetsListBoxes() } } +//----------------------------------------------------------------------------- +// [SECTION] DemoWindowWidgetsLiveEdit() +//----------------------------------------------------------------------------- + +static void DemoWindowWidgetsLiveEdit(ImGuiDemoWindowData* demo_data) +{ + if (ImGui::TreeNode("Live Edit Flags")) + { + IMGUI_DEMO_MARKER("Widgets/Live Edit Flgs"); + + ImGui::TextWrapped("Select whether to apply keyboard edits to backing variables _while_ typing."); + + ImGui::Checkbox("Override Live Edit Flags in Demo Window", &demo_data->LiveEditOverride); + if (!demo_data->LiveEditOverride) + demo_data->LiveEditFlags = ImGui::GetItemFlags(); + + ImGui::BeginDisabled(demo_data->LiveEditOverride == false); + ImGui::Indent(); + ImGui::CheckboxFlags("ImGuiItemFlags_LiveEditOnInputText", &demo_data->LiveEditFlags, ImGuiItemFlags_LiveEditOnInputText); + ImGui::CheckboxFlags("ImGuiItemFlags_LiveEditOnInputScalar", &demo_data->LiveEditFlags, ImGuiItemFlags_LiveEditOnInputScalar); + ImGui::Unindent(); + ImGui::EndDisabled(); + + ImGui::Text("Try typing '123' and seeing effect on backing value:"); + static char str[32] = ""; + ImGui::InputText("str", str, IM_COUNTOF(str)); + ImGui::Text("Backing value: \"%s\"", str); + static int i = 0; + ImGui::InputInt("int", &i, 0, 0); + ImGui::Text("Backing value: %d", i); + static float f = 0.0f; + ImGui::SliderFloat("float", &f, 0.0f, 100.0f); + ImGui::Text("Backing value: %f", f); + + ImGui::TreePop(); + } +} + //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsMultiComponents() //----------------------------------------------------------------------------- @@ -2156,7 +2201,7 @@ static void DemoWindowWidgetsProgressBars() char buf[32]; sprintf(buf, "%d/%d", (int)(progress * 1753), 1753); - ImGui::ProgressBar(progress, ImVec2(0.f, 0.f), buf); + ImGui::ProgressBar(progress, ImVec2(0.0f, 0.0f), buf); // Pass an animated negative value, e.g. -1.0f * (float)ImGui::GetTime() is the recommended value. // Adjust the factor if you want to adjust the animation speed. @@ -2185,10 +2230,28 @@ static void DemoWindowWidgetsQueryingStatuses() }; static int item_type = 4; static bool item_disabled = false; + static bool liveedit_flags_override = false; + static ImGuiItemFlags liveedit_flags = 0; ImGui::Combo("Item Type", &item_type, item_names, IM_COUNTOF(item_names), IM_COUNTOF(item_names)); ImGui::SameLine(); HelpMarker("Testing how various types of items are interacting with the IsItemXXX functions. Note that the bool return value of most ImGui function is generally equivalent to calling ImGui::IsItemHovered()."); ImGui::Checkbox("Item Disabled", &item_disabled); + ImGui::Checkbox("Override LiveEdit:", &liveedit_flags_override); + ImGui::SameLine(); + if (!liveedit_flags_override) + liveedit_flags = ImGui::GetItemFlags(); + ImGui::BeginDisabled(liveedit_flags_override == false); + ImGui::CheckboxFlags("_LiveEditOnInput", &liveedit_flags, ImGuiItemFlags_LiveEditOnInput); + ImGui::SameLine(); + ImGui::CheckboxFlags("_LiveEditOnInputText", &liveedit_flags, ImGuiItemFlags_LiveEditOnInputText); + ImGui::SameLine(); + ImGui::CheckboxFlags("_LiveEditOnInputScalar", &liveedit_flags, ImGuiItemFlags_LiveEditOnInputScalar); + ImGui::EndDisabled(); + if (liveedit_flags_override) + { + ImGui::PushItemFlag(ImGuiItemFlags_LiveEditOnInputText, (liveedit_flags & ImGuiItemFlags_LiveEditOnInputText) != 0); + ImGui::PushItemFlag(ImGuiItemFlags_LiveEditOnInputScalar, (liveedit_flags & ImGuiItemFlags_LiveEditOnInputScalar) != 0); + } // Submit selected items so we can query their status in the code following it. bool ret = false; @@ -2275,6 +2338,11 @@ static void DemoWindowWidgetsQueryingStatuses() "IsItemHovered(_Tooltip) = %d", hovered_delay_none, hovered_delay_stationary, hovered_delay_short, hovered_delay_normal, hovered_delay_tooltip); + if (liveedit_flags_override) + { + ImGui::PopItemFlag(); + ImGui::PopItemFlag(); + } if (item_disabled) ImGui::EndDisabled(); @@ -4121,7 +4189,7 @@ static void DemoWindowWidgetsTooltips() ImGui::BeginDisabled(); ImGui::Button("Disabled item", sz); if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip)) - ImGui::SetTooltip("I am a a tooltip for a disabled item."); + ImGui::SetTooltip("I am a tooltip for a disabled item."); ImGui::EndDisabled(); ImGui::TreePop(); @@ -4138,9 +4206,9 @@ static void DemoWindowWidgetsTreeNodes() { IMGUI_DEMO_MARKER("Widgets/Tree Nodes"); // See see "Examples -> Property Editor" (ShowExampleAppPropertyEditor() function) for a fancier, data-driven tree. - if (ImGui::TreeNode("Basic trees")) + if (ImGui::TreeNode("Basic Trees")) { - IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Basic trees"); + IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Basic Trees"); for (int i = 0; i < 5; i++) { // Use SetNextItemOpen() so set the default state of a node to be open. We could @@ -4164,9 +4232,9 @@ static void DemoWindowWidgetsTreeNodes() ImGui::TreePop(); } - if (ImGui::TreeNode("Hierarchy lines")) + if (ImGui::TreeNode("Hierarchy Lines")) { - IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Hierarchy lines"); + IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Hierarchy Lines"); static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DefaultOpen; HelpMarker("Default option for DrawLinesXXX is stored in style.TreeLinesFlags"); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesNone", &base_flags, ImGuiTreeNodeFlags_DrawLinesNone); @@ -4203,15 +4271,57 @@ static void DemoWindowWidgetsTreeNodes() ImGui::TreePop(); } - if (ImGui::TreeNode("Advanced, with Selectable nodes")) + if (ImGui::TreeNode("Selectable Nodes")) { - IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Advanced, with Selectable nodes"); + IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Selectable Nodes"); HelpMarker( - "This is a more typical looking tree with selectable nodes.\n" - "Click to select, Ctrl+Click to toggle, click on arrows or double-click to open."); + "Manually implemented selectable nodes.\n" + "Click to select, Ctrl+Click to toggle, click on arrows or double-click to open.\n\n" + "You may also use the multi-select API (see 'Demo->Widgets->Selection State & Multi-Select') for more advanced multi-selection features."); + + // Hold in 'selection_mask' a simple representation of what may be user-side selection state. + // - You may retain selection state inside or outside your objects in whatever format you see fit. + // You may use ImGuiSelectionBasicStorage which is conceptually close to a set<> of identifiers. + // - We record which node was clicked and then apply selection at the end of the loop. + // - This is a manual and simplified reimplementation of multi-selection, which the full + // BeginMultiSelect() API implements better, but which is not trivial to wire for trees. + static int selection_mask = 0x00; + int node_clicked_idx = -1; + for (int node_n = 0; node_n < 6; node_n++) + { + // Disable the default "open on single-click behavior" + set Selected flag according to our selection. + // To alter selection we use if 'IsItemClicked() && !IsItemToggledOpen()', so clicking on an arrow doesn't alter selection. + // In a BeginMultiSelect()/EndMultiSelect() we could use IsItemToggledSelection() but here we reimplement and use our own logic. + ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth; + if (selection_mask & (1 << node_n)) + flags |= ImGuiTreeNodeFlags_Selected; + + bool is_open = ImGui::TreeNodeEx((void*)(intptr_t)node_n, flags, "Selectable Node %d", node_n); + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) + node_clicked_idx = node_n; + if (is_open) + { + ImGui::BulletText(""); + ImGui::TreePop(); + } + } + if (node_clicked_idx != -1) + { + // Update selection state (process outside of tree loop to avoid visual inconsistencies during the clicking frame) + if (ImGui::GetIO().KeyCtrl) + selection_mask ^= (1 << node_clicked_idx); // Ctrl+Click to toggle + else //if (!(selection_mask & (1 << node_clicked_idx))) // Depending on selection behavior you want, may want to preserve selection when clicking on item that is part of the selection + selection_mask = (1 << node_clicked_idx); // Click to single-select + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Advanced")) + { + IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Advanced"); static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth; static bool align_label_with_current_x_position = false; - static bool test_drag_and_drop = false; + static bool use_drag_and_drop = false; ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnArrow", &base_flags, ImGuiTreeNodeFlags_OpenOnArrow); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnDoubleClick", &base_flags, ImGuiTreeNodeFlags_OpenOnDoubleClick); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAvailWidth", &base_flags, ImGuiTreeNodeFlags_SpanAvailWidth); ImGui::SameLine(); HelpMarker("Extend hit area to all available width instead of allowing more items to be laid out after the node."); @@ -4229,44 +4339,30 @@ static void DemoWindowWidgetsTreeNodes() ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesToNodes", &base_flags, ImGuiTreeNodeFlags_DrawLinesToNodes); ImGui::Checkbox("Align label with current X position", &align_label_with_current_x_position); - ImGui::Checkbox("Test tree node as drag source", &test_drag_and_drop); - ImGui::Text("Hello!"); + ImGui::Checkbox("Make Tree Nodes as drag & drop sources", &use_drag_and_drop); if (align_label_with_current_x_position) ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing()); - // 'selection_mask' is dumb representation of what may be user-side selection state. - // You may retain selection state inside or outside your objects in whatever format you see fit. - // 'node_clicked' is temporary storage of what node we have clicked to process selection at the end - /// of the loop. May be a pointer to your own node type, etc. - static int selection_mask = (1 << 2); - int node_clicked = -1; - for (int i = 0; i < 6; i++) + for (int node_n = 0; node_n < 6; node_n++) { - // Disable the default "open on single-click behavior" + set Selected flag according to our selection. - // To alter selection we use IsItemClicked() && !IsItemToggledOpen(), so clicking on an arrow doesn't alter selection. ImGuiTreeNodeFlags node_flags = base_flags; - const bool is_selected = (selection_mask & (1 << i)) != 0; - if (is_selected) - node_flags |= ImGuiTreeNodeFlags_Selected; - if (i < 3) + if (node_n < 3) { // Items 0..2 are Tree Node - bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Node %d", i); - if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) - node_clicked = i; - if (test_drag_and_drop && ImGui::BeginDragDropSource()) + bool is_open = ImGui::TreeNodeEx((void*)(intptr_t)node_n, node_flags, "Selectable Node %d", node_n); + if (use_drag_and_drop && ImGui::BeginDragDropSource()) { - ImGui::SetDragDropPayload("_TREENODE", NULL, 0); + ImGui::SetDragDropPayload("MY_TREENODE_PAYLOAD_TYPE", NULL, 0); ImGui::Text("This is a drag and drop source"); ImGui::EndDragDropSource(); } - if (i == 2 && (base_flags & ImGuiTreeNodeFlags_SpanLabelWidth)) + if (node_n == 2 && (base_flags & ImGuiTreeNodeFlags_SpanLabelWidth)) { // Item 2 has an additional inline button to help demonstrate SpanLabelWidth. ImGui::SameLine(); if (ImGui::SmallButton("button")) {} } - if (node_open) + if (is_open) { ImGui::BulletText("Blah blah\nBlah Blah"); ImGui::SameLine(); @@ -4280,26 +4376,15 @@ static void DemoWindowWidgetsTreeNodes() // The only reason we use TreeNode at all is to allow selection of the leaf. Otherwise we can // use BulletText() or advance the cursor by GetTreeNodeToLabelSpacing() and call Text(). node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet - ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Leaf %d", i); - if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) - node_clicked = i; - if (test_drag_and_drop && ImGui::BeginDragDropSource()) + ImGui::TreeNodeEx((void*)(intptr_t)node_n, node_flags, "Selectable Leaf %d", node_n); + if (use_drag_and_drop && ImGui::BeginDragDropSource()) { - ImGui::SetDragDropPayload("_TREENODE", NULL, 0); + ImGui::SetDragDropPayload("MY_TREENODE_PAYLOAD_TYPE", NULL, 0); ImGui::Text("This is a drag and drop source"); ImGui::EndDragDropSource(); } } } - if (node_clicked != -1) - { - // Update selection state - // (process outside of tree loop to avoid visual inconsistencies during the clicking frame) - if (ImGui::GetIO().KeyCtrl) - selection_mask ^= (1 << node_clicked); // Ctrl+Click to toggle - else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, may want to preserve selection when clicking on item that is part of the selection - selection_mask = (1 << node_clicked); // Click to single-select - } if (align_label_with_current_x_position) ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing()); ImGui::TreePop(); @@ -4392,8 +4477,14 @@ static void DemoWindowWidgets(ImGuiDemoWindowData* demo_data) // IMGUI_DEMO_MARKER("Widgets"); const bool disable_all = demo_data->DisableSections; // The Checkbox for that is inside the "Disabled" section at the bottom + const bool override_liveedit = demo_data->LiveEditOverride; if (disable_all) ImGui::BeginDisabled(); + if (override_liveedit) + { + ImGui::PushItemFlag(ImGuiItemFlags_LiveEditOnInputText, (demo_data->LiveEditFlags & ImGuiItemFlags_LiveEditOnInputText) != 0); + ImGui::PushItemFlag(ImGuiItemFlags_LiveEditOnInputScalar, (demo_data->LiveEditFlags & ImGuiItemFlags_LiveEditOnInputScalar) != 0); + } DemoWindowWidgetsBasic(); DemoWindowWidgetsBullets(); @@ -4413,6 +4504,7 @@ static void DemoWindowWidgets(ImGuiDemoWindowData* demo_data) DemoWindowWidgetsFonts(); DemoWindowWidgetsImages(); DemoWindowWidgetsListBoxes(); + DemoWindowWidgetsLiveEdit(demo_data); DemoWindowWidgetsMultiComponents(); DemoWindowWidgetsPlotting(); DemoWindowWidgetsProgressBars(); @@ -4427,6 +4519,11 @@ static void DemoWindowWidgets(ImGuiDemoWindowData* demo_data) DemoWindowWidgetsTreeNodes(); DemoWindowWidgetsVerticalSliders(); + if (override_liveedit) + { + ImGui::PopItemFlag(); + ImGui::PopItemFlag(); + } if (disable_all) ImGui::EndDisabled(); } @@ -5071,7 +5168,8 @@ static void DemoWindowLayout() // If you want to create your own time line for a real application you may be better off manipulating // the cursor position yourself, aka using SetCursorPos/SetCursorScreenPos to position the widgets // yourself. You may also want to use the lower-level ImDrawList API. - int num_buttons = 10 + ((line & 1) ? line * 9 : line * 3); + const int num_buttons = 10 + ((line & 1) ? line * 9 : line * 3); + const float base_w = ImGui::GetFontSize() * 3; for (int n = 0; n < num_buttons; n++) { if (n > 0) ImGui::SameLine(); @@ -5083,7 +5181,7 @@ static void DemoWindowLayout() ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(hue, 0.6f, 0.6f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(hue, 0.7f, 0.7f)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(hue, 0.8f, 0.8f)); - ImGui::Button(label, ImVec2(40.0f + sinf((float)(line + n)) * 20.0f, 0.0f)); + ImGui::Button(label, ImVec2(base_w + sinf((float)(line + n)) * base_w * 0.5f, 0.0f)); ImGui::PopStyleColor(3); ImGui::PopID(); } @@ -8539,6 +8637,8 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f"); SliderFloat("PopupRounding", &style.PopupRounding, 0.0f, 12.0f, "%.0f"); SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f"); + SliderFloat("MenuItemRounding", &style.MenuItemRounding, 0.0f, 12.0f, "%.0f"); + // NB: SelectableRounding is intentionally NOT made visible here. We don't want to encourage people using that. SeparatorText("Scrollbar"); SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f"); @@ -8994,7 +9094,7 @@ struct ExampleAppConsole static int Stricmp(const char* s1, const char* s2) { int d; while ((d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; } return d; } static int Strnicmp(const char* s1, const char* s2, int n) { int d = 0; while (n > 0 && (d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; n--; } return d; } static char* Strdup(const char* s) { IM_ASSERT(s); size_t len = strlen(s) + 1; void* buf = ImGui::MemAlloc(len); IM_ASSERT(buf); return (char*)memcpy(buf, (const void*)s, len); } - static void Strtrim(char* s) { char* str_end = s + strlen(s); while (str_end > s && str_end[-1] == ' ') str_end--; *str_end = 0; } + static void Strtrimblanks(char* s) { char* str_end = s + strlen(s); while (str_end > s && str_end[-1] == ' ') str_end--; *str_end = 0; } void ClearLog() { @@ -9143,10 +9243,10 @@ struct ExampleAppConsole if (ImGui::InputText("Input", InputBuf, IM_COUNTOF(InputBuf), input_text_flags, &TextEditCallbackStub, (void*)this)) { char* s = InputBuf; - Strtrim(s); + Strtrimblanks(s); if (s[0]) ExecCommand(s); - strcpy(s, ""); + s[0] = 0; reclaim_focus = true; } @@ -10165,6 +10265,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) { IMGUI_DEMO_MARKER("Examples/Custom rendering/Primitives"); ImGui::PushItemWidth(-ImGui::GetFontSize() * 15); + ImGui::PushItemFlag(ImGuiItemFlags_LiveEditOnInput, true); ImDrawList* draw_list = ImGui::GetWindowDrawList(); // Draw gradients @@ -10191,7 +10292,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) // Draw a bunch of primitives ImGui::Text("All primitives"); - static float sz = 36.0f; + static float sz = 42.0f; static float thickness = 3.0f; static int ngon_sides = 6; static bool circle_segments_override = false; @@ -10285,6 +10386,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) x += sz + spacing; ImGui::Dummy(ImVec2((sz + spacing) * 13.2f, (sz + spacing) * 3.0f)); + ImGui::PopItemFlag(); ImGui::PopItemWidth(); ImGui::EndTabItem(); } diff --git a/3rdparty/imgui/src/imgui_draw.cpp b/3rdparty/imgui/src/imgui_draw.cpp index c178778f07..2eed77033b 100644 --- a/3rdparty/imgui/src/imgui_draw.cpp +++ b/3rdparty/imgui/src/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 +// dear imgui, v1.92.9b // (drawing and font code) /* @@ -402,7 +402,6 @@ ImDrawListSharedData::ImDrawListSharedData() const float a = ((float)i * 2 * IM_PI) / (float)IM_COUNTOF(ArcFastVtx); ArcFastVtx[i] = ImVec2(ImCos(a), ImSin(a)); } - ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError); } ImDrawListSharedData::~ImDrawListSharedData() @@ -412,17 +411,17 @@ ImDrawListSharedData::~ImDrawListSharedData() void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error) { - if (CircleSegmentMaxError == max_error) + if (CircleTessellationMaxError == max_error) return; IM_ASSERT(max_error > 0.0f); - CircleSegmentMaxError = max_error; + CircleTessellationMaxError = max_error; for (int i = 0; i < IM_COUNTOF(CircleSegmentCounts); i++) { const float radius = (float)i; - CircleSegmentCounts[i] = (ImU8)((i > 0) ? IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, CircleSegmentMaxError) : IM_DRAWLIST_ARCFAST_SAMPLE_MAX); + CircleSegmentCounts[i] = (ImU8)((i > 0) ? IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, CircleTessellationMaxError) : IM_DRAWLIST_ARCFAST_SAMPLE_MAX); } - ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError); + ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleTessellationMaxError); } ImDrawList::ImDrawList(ImDrawListSharedData* shared_data) @@ -656,11 +655,11 @@ void ImDrawList::_OnChangedVtxOffset() int ImDrawList::_CalcCircleAutoSegmentCount(float radius) const { // Automatic segment count - const int radius_idx = (int)(radius + 0.999999f); // ceil to never reduce accuracy + const int radius_idx = (int)(radius + 0.999f); // ceil to never reduce accuracy if (radius_idx >= 0 && radius_idx < IM_COUNTOF(_Data->CircleSegmentCounts)) return _Data->CircleSegmentCounts[radius_idx]; // Use cached value else - return IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleSegmentMaxError); + return IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleTessellationMaxError); } // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling) @@ -823,7 +822,12 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 const ImVec2 opaque_uv = _Data->TexUvWhitePixel; const int count = closed ? points_count : points_count - 1; // The number of line segments we need to draw const bool thick_line = (thickness > _FringeScale); - IM_ASSERT((flags & ImDrawFlags_InvalidMask_) == 0 && "Incorrect parameter. Did you swapped 'thickness' and 'flags'?"); + + // If this assert triggers on legacy code: + // - 1.92.8 (2025/05): swapped two last parameters order: flags, thickness --> thickness, flags. This should normally be caught by compile-time type-checking. + // - 1.92.8 (2025/05): changed value of ImDrawList_Closed which was previously guaranteed to be == 1. Hardcoded use of 1 or true should be replaced. + // Read more details near AddRect() + see "API BREAKING CHANGES" section for 1.82, 1.90 and 1.92.8. + IM_ASSERT_USER_ERROR_RET((flags & ImDrawFlags_InvalidMask_) == 0, "Incorrect parameter. Did you swap 'thickness' and 'flags'?"); if (Flags & ImDrawListFlags_AntiAliasedLines) { @@ -1475,27 +1479,24 @@ void ImDrawList::AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float th { if ((col & IM_COL32_A_MASK) == 0) return; - PathLineTo(p1 + ImVec2(0.5f, 0.5f)); - PathLineTo(p2 + ImVec2(0.5f, 0.5f)); - PathStroke(col, thickness); + const ImVec2 points[2] = { ImVec2(p1.x + 0.5f, p1.y + 0.5f), ImVec2(p2.x + 0.5f, p2.y + 0.5f) }; + AddPolyline(points, 2, col, thickness); } void ImDrawList::AddLineH(float min_x, float max_x, float y, ImU32 col, float thickness) { if ((col & IM_COL32_A_MASK) == 0) return; - PathLineTo(ImVec2(min_x + 0.5f, y + 0.5f)); // Same as AddLine() above. - PathLineTo(ImVec2(max_x + 0.5f, y + 0.5f)); - PathStroke(col, thickness); + const ImVec2 points[2] = { ImVec2(min_x + 0.5f, y + 0.5f), ImVec2(max_x + 0.5f, y + 0.5f) }; // Same as AddLine() above. + AddPolyline(points, 2, col, thickness); } void ImDrawList::AddLineV(float x, float min_y, float max_y, ImU32 col, float thickness) { if ((col & IM_COL32_A_MASK) == 0) return; - PathLineTo(ImVec2(x + 0.5f, min_y + 0.5f)); // Same as AddLine() above. - PathLineTo(ImVec2(x + 0.5f, max_y + 0.5f)); - PathStroke(col, thickness); + const ImVec2 points[2] = { ImVec2(x + 0.5f, min_y + 0.5f), ImVec2(x + 0.5f, max_y + 0.5f) }; // Same as AddLine() above. + AddPolyline(points, 2, col, thickness); } // p_min = upper-left, p_max = lower-right @@ -1503,14 +1504,15 @@ void ImDrawList::AddLineV(float x, float min_y, float max_y, ImU32 col, float th void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, float thickness, ImDrawFlags flags) { // If this assert triggers on legacy code: - // - 1.92.8 (2025/04): swapped two last parameters order: flags, thickness --> thickness, flags. This should normally be caught by compile-time type-checking. + // - 1.92.8 (2025/05): swapped two last parameters order: flags, thickness --> thickness, flags. This should normally be caught by compile-time type-checking. + // - 1.92.8 (2025/05): changed value of ImDrawList_Closed which was previously guaranteed to be == 1. Hardcoded use of 1 or true should be replaced. // - 1.82.0 (2021/03): changed ImDrawCornerFlags to ImDrawFlags_RoundCornersXXX values. // If you used hard-coded 1 to 15 or ~0 in flags to configure corner rounding use the new flags! // - Hard coded support for ~0 == ImDrawFlags_RoundCornersAll. // - Hard coded support for values 0x01 to 0x0F (matching 15 out of 16 old flags combinations) --> see FixRectCornerFlags() in <1.90 code. // - Hard coded 0x00 with 'float rounding > 0.0f' --> replace with ImDrawFlags_RoundCornersNone or use 'float rounding = 0.0f'. - // See "API BREAKING CHANGES" section for 1.82 and 1.90. - IM_ASSERT((flags & ImDrawFlags_InvalidMask_) == 0 && "Incorrect parameter. Did you swapped 'thickness' and 'flags'?"); // Or misuse of legacy hard-coded ImDrawCornerFlags values + // See "API BREAKING CHANGES" section for 1.82, 1.90 and 1.92.8. + IM_ASSERT_USER_ERROR_RET((flags & ImDrawFlags_InvalidMask_) == 0, "Incorrect parameter. Did you swap 'thickness' and 'flags'?"); // Or misuse of legacy hard-coded ImDrawCornerFlags values if ((col & IM_COL32_A_MASK) == 0) return; @@ -2278,11 +2280,14 @@ void ImDrawListSplitter::SetCurrentChannel(ImDrawList* draw_list, int idx) void ImDrawData::Clear() { Valid = false; - CmdListsCount = TotalIdxCount = TotalVtxCount = 0; + FrameCount = TotalIdxCount = TotalVtxCount = 0; CmdLists.resize(0); // The ImDrawList are NOT owned by ImDrawData but e.g. by ImGuiContext, so we don't clear them. DisplayPos = DisplaySize = FramebufferScale = ImVec2(0.0f, 0.0f); OwnerViewport = NULL; Textures = NULL; +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + CmdListsCount = 0; +#endif } // Important: 'out_list' is generally going to be draw_data->CmdLists, but may be another temporary list @@ -2327,14 +2332,12 @@ void ImGui::AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector // Add to output list + records state in ImDrawData out_list->push_back(draw_list); - draw_data->CmdListsCount++; draw_data->TotalVtxCount += draw_list->VtxBuffer.Size; draw_data->TotalIdxCount += draw_list->IdxBuffer.Size; } void ImDrawData::AddDrawList(ImDrawList* draw_list) { - IM_ASSERT(CmdLists.Size == CmdListsCount); draw_list->_PopUnusedDrawCmd(); ImGui::AddDrawListToDrawDataEx(this, &CmdLists, draw_list); } @@ -2443,7 +2446,7 @@ ImFontConfig::ImFontConfig() GlyphMaxAdvanceX = FLT_MAX; RasterizerMultiply = 1.0f; RasterizerDensity = 1.0f; - LineHeight = 1.0f; + LineHeight = 1.0f; // [PCSX2 Custom] EllipsisChar = 0; } @@ -2561,8 +2564,6 @@ void ImTextureData::DestroyPixels() // - ImFontAtlasBuildPreloadAllGlyphRanges() // - ImFontAtlasBuildUpdatePointers() // - ImFontAtlasBuildRenderBitmapFromString() -// - ImFontAtlasBuildUpdateBasicTexData() -// - ImFontAtlasBuildUpdateLinesTexData() // - ImFontAtlasBuildAddFont() // - ImFontAtlasBuildSetupFontBakedEllipsis() // - ImFontAtlasBuildSetupFontBakedBlanks() @@ -2579,13 +2580,14 @@ void ImTextureData::DestroyPixels() // - ImFontAtlasUpdateDrawListsSharedData() //----------------------------------------------------------------------------- // - ImFontAtlasBuildSetTexture() -// - ImFontAtlasBuildAddTexture() -// - ImFontAtlasBuildMakeSpace() -// - ImFontAtlasBuildRepackTexture() -// - ImFontAtlasBuildGrowTexture() -// - ImFontAtlasBuildRepackOrGrowTexture() -// - ImFontAtlasBuildGetTextureSizeEstimate() -// - ImFontAtlasBuildCompactTexture() +// - ImFontAtlasBuildUpdateTexData() +// - ImFontAtlasTextureAdd() +// - ImFontAtlasTextureRepack() +// - ImFontAtlasTextureGrow() +// - ImFontAtlasTextureMakeSpace() +// - ImFontAtlasTextureGetSizeEstimate() +// - ImFontAtlasBuildClear() +// - ImFontAtlasTextureCompact() // - ImFontAtlasBuildInit() // - ImFontAtlasBuildDestroy() //----------------------------------------------------------------------------- @@ -2690,6 +2692,7 @@ ImFontAtlas::~ImFontAtlas() // Calling this mid-frame will discard the CPU-side copy of the texture data which is generally unreliable as you may have textures queued for creation or updates. void ImFontAtlas::Clear() { + IMGUI_DEBUG_LOG_FONT("[font] ImFontAtlas::Clear()\n"); bool backup_renderer_has_textures = RendererHasTextures; RendererHasTextures = false; // Full Clear() is supported, but ClearTexData() only isn't. ClearFonts(); @@ -2700,6 +2703,7 @@ void ImFontAtlas::Clear() void ImFontAtlas::ClearFonts() { // FIXME-NEWATLAS: Illegal to remove currently bound font. + IMGUI_DEBUG_LOG_FONT("[font] ImFontAtlas::ClearFonts()\n"); IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!"); for (ImFont* font : Fonts) ImFontAtlasBuildNotifySetFont(this, font, NULL); @@ -2775,6 +2779,28 @@ void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool rendere IM_ASSERT(atlas->Builder == NULL || atlas->Builder->FrameCount < frame_count); // Protection against being called twice. atlas->RendererHasTextures = renderer_has_textures; + // Update texture status and discard old textures. + // (we do this first thing to handle an edge case: if user mistakenly calls ClearFonts()+SetStatus(OK) during + // rendering, it would ImFontAtlasBuildMain() rebuilding before tex->Updates[] gets a chance to be cleared) + // (if somehow we need to move this back lower in the function, we could manually call the code to clear Updates[]). + for (int tex_n = 0; tex_n < atlas->TexList.Size; tex_n++) + { + // Update and remove if requested + ImTextureData* tex = atlas->TexList[tex_n]; + if (tex->Status == ImTextureStatus_WantCreate && atlas->RendererHasTextures) + IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && "Backend set texture's TexID/BackendUserData but did not update Status to OK."); + + bool remove_from_list = ImTextureDataUpdateNewFrame(tex); + if (remove_from_list) + { + IM_ASSERT(atlas->TexData != tex); + tex->DestroyPixels(); + IM_DELETE(tex); + atlas->TexList.erase(atlas->TexList.begin() + tex_n); + tex_n--; + } + } + // Check that font atlas was built or backend support texture reload in which case we can build now if (atlas->RendererHasTextures) { @@ -2814,61 +2840,50 @@ void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool rendere builder->BakedPool.Size -= builder->BakedDiscardedCount; builder->BakedDiscardedCount = 0; } +} - // Update texture status - for (int tex_n = 0; tex_n < atlas->TexList.Size; tex_n++) +bool ImTextureDataUpdateNewFrame(ImTextureData* tex) +{ + bool remove_from_list = false; + if (tex->Status == ImTextureStatus_OK) { - ImTextureData* tex = atlas->TexList[tex_n]; - bool remove_from_list = false; - if (tex->Status == ImTextureStatus_OK) - { - tex->Updates.resize(0); - tex->UpdateRect.x = tex->UpdateRect.y = (unsigned short)~0; - tex->UpdateRect.w = tex->UpdateRect.h = 0; - } - if (tex->Status == ImTextureStatus_WantCreate && atlas->RendererHasTextures) - IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && "Backend set texture's TexID/BackendUserData but did not update Status to OK."); - - // Request destroy - // - Keep bool to true in order to differentiate a planned destroy vs a destroy decided by the backend. - // - We don't destroy pixels right away, as backend may have an in-flight copy from RAM. - if (tex->WantDestroyNextFrame && tex->Status != ImTextureStatus_Destroyed && tex->Status != ImTextureStatus_WantDestroy) - { - IM_ASSERT(tex->Status == ImTextureStatus_OK || tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates); - tex->Status = ImTextureStatus_WantDestroy; - } - - // If a texture has never reached the backend, they don't need to know about it. - // (note: backends between 1.92.0 and 1.92.4 could set an already destroyed texture to ImTextureStatus_WantDestroy - // when invalidating graphics objects twice, which would previously remove it from the list and crash.) - if (tex->Status == ImTextureStatus_WantDestroy && tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL) - tex->Status = ImTextureStatus_Destroyed; - - // Process texture being destroyed - if (tex->Status == ImTextureStatus_Destroyed) - { - IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && "Backend set texture Status to Destroyed but did not clear TexID/BackendUserData!"); - if (tex->WantDestroyNextFrame) - remove_from_list = true; // Destroy was scheduled by us - else - tex->Status = ImTextureStatus_WantCreate; // Destroy was done was backend: recreate it (e.g. freed resources mid-run) - } - - // The backend may need defer destroying by a few frames, to handle texture used by previous in-flight rendering. - // We allow the texture staying in _WantDestroy state and increment a counter which the backend can use to take its decision. - if (tex->Status == ImTextureStatus_WantDestroy) - tex->UnusedFrames++; - - // Destroy and remove - if (remove_from_list) - { - IM_ASSERT(atlas->TexData != tex); - tex->DestroyPixels(); - IM_DELETE(tex); - atlas->TexList.erase(atlas->TexList.begin() + tex_n); - tex_n--; - } + tex->Updates.resize(0); + tex->UpdateRect.x = tex->UpdateRect.y = (unsigned short)~0; + tex->UpdateRect.w = tex->UpdateRect.h = 0; } + + // Request destroy + // - Keep bool to true in order to differentiate a planned destroy vs a destroy decided by the backend. + // - We don't destroy pixels right away, as backend may have an in-flight copy from RAM. + if (tex->WantDestroyNextFrame && tex->Status != ImTextureStatus_Destroyed && tex->Status != ImTextureStatus_WantDestroy) + { + IM_ASSERT(tex->Status == ImTextureStatus_OK || tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates); + tex->Status = ImTextureStatus_WantDestroy; + } + + // If a texture has never reached the backend, they don't need to know about it. + // (note: backends between 1.92.0 and 1.92.4 could set an already destroyed texture to ImTextureStatus_WantDestroy + // when invalidating graphics objects twice, which would previously remove it from the list and crash.) + if (tex->Status == ImTextureStatus_WantDestroy && tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && tex->QueueUserData == NULL) + tex->Status = ImTextureStatus_Destroyed; + + // Process texture being destroyed + if (tex->Status == ImTextureStatus_Destroyed) + { + IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && "Backend set texture Status to Destroyed but did not clear TexID/BackendUserData!"); + IM_ASSERT(tex->QueueUserData == NULL && "Texture queue set Status to Destroyed but did not clear QueueUserData!"); + if (tex->WantDestroyNextFrame) + remove_from_list = true; // Destroy was scheduled by us + else + tex->Status = ImTextureStatus_WantCreate; // Destroy was done was backend: recreate it (e.g. freed resources mid-run) + } + + // The backend may need defer destroying by a few frames, to handle texture used by previous in-flight rendering. + // We allow the texture staying in _WantDestroy state and increment a counter which the backend can use to take its decision. + if (tex->Status == ImTextureStatus_WantDestroy) + tex->UnusedFrames++; + + return remove_from_list; } void ImFontAtlasTextureBlockConvert(const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch, unsigned char* dst_pixels, ImTextureFormat dst_fmt, int dst_pitch, int w, int h) @@ -3071,7 +3086,7 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg_in) font->Flags = font_cfg_in->Flags; font->LegacySize = font_cfg_in->SizePixels; font->CurrentRasterizerDensity = font_cfg_in->RasterizerDensity; - font->LineHeight = font_cfg_in->LineHeight; + font->LineHeight = font_cfg_in->LineHeight; // [PCSX2 Custom] Fonts.push_back(font); } else @@ -3145,9 +3160,11 @@ static void Decode85(const unsigned char* src, unsigned char* dst) dst += 4; } } -#ifndef IMGUI_DISABLE_DEFAULT_FONT -static const char* GetDefaultCompressedFontDataProggyClean(int* out_size); -static const char* GetDefaultCompressedFontDataProggyForever(int* out_size); +#if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_BITMAP) +const char* ImGui_GetDefaultCompressedFontDataProggyClean(int* out_size); +#endif +#if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_VECTOR) +const char* ImGui_GetDefaultCompressedFontDataProggyForever(int* out_size); #endif // This duplicates some of the logic in UpdateFontsNewFrame() which is a bit chicken-and-eggy/tricky to extract due to variety of codepaths and possible initialization ordering. @@ -3171,7 +3188,7 @@ ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg) // If you want a similar font which may be better scaled, consider using AddFontDefaultVector(). ImFont* ImFontAtlas::AddFontDefaultBitmap(const ImFontConfig* font_cfg_template) { -#ifndef IMGUI_DISABLE_DEFAULT_FONT +#if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_BITMAP) ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); if (!font_cfg_template) font_cfg.PixelSnapH = true; // Prevents sub-integer scaling factors at lower-level layers. @@ -3186,20 +3203,20 @@ ImFont* ImFontAtlas::AddFontDefaultBitmap(const ImFontConfig* font_cfg_template) font_cfg.GlyphOffset.y += 1.0f * (font_cfg.SizePixels / 13.0f); // Add +1 offset per 13 units int ttf_compressed_size = 0; - const char* ttf_compressed = GetDefaultCompressedFontDataProggyClean(&ttf_compressed_size); + const char* ttf_compressed = ImGui_GetDefaultCompressedFontDataProggyClean(&ttf_compressed_size); return AddFontFromMemoryCompressedTTF(ttf_compressed, ttf_compressed_size, font_cfg.SizePixels, &font_cfg); #else IM_ASSERT(0 && "Function is disabled in this build."); IM_UNUSED(font_cfg_template); return NULL; -#endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT +#endif } // Load a minimal version of ProggyForever, designed to match our good old ProggyClean, but nicely scalable. // (See build script in https://github.com/ocornut/proggyforever for details) ImFont* ImFontAtlas::AddFontDefaultVector(const ImFontConfig* font_cfg_template) { -#ifndef IMGUI_DISABLE_DEFAULT_FONT +#if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_VECTOR) ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); if (!font_cfg_template) font_cfg.PixelSnapH = true; // Precisely match ProggyClean, but prevents sub-integer scaling factors at lower-level layers. @@ -3214,13 +3231,13 @@ ImFont* ImFontAtlas::AddFontDefaultVector(const ImFontConfig* font_cfg_template) font_cfg.GlyphOffset.y += 0.5f * (font_cfg.SizePixels / 16.0f); // Closer match ProggyClean + avoid descenders going too high (with current code). int ttf_compressed_size = 0; - const char* ttf_compressed = GetDefaultCompressedFontDataProggyForever(&ttf_compressed_size); + const char* ttf_compressed = ImGui_GetDefaultCompressedFontDataProggyForever(&ttf_compressed_size); return AddFontFromMemoryCompressedTTF(ttf_compressed, ttf_compressed_size, font_cfg.SizePixels, &font_cfg); #else IM_ASSERT(0 && "Function is disabled in this build."); IM_UNUSED(font_cfg_template); return NULL; -#endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT +#endif } ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) @@ -3576,7 +3593,7 @@ void ImFontAtlasBuildRenderBitmapFromString(ImFontAtlas* atlas, int x, int y, in } } -static void ImFontAtlasBuildUpdateBasicTexData(ImFontAtlas* atlas) +static void ImFontAtlasBuildUpdateTexDataBasic(ImFontAtlas* atlas) { // Pack and store identifier so we can refresh UV coordinates on texture resize. // FIXME-NEWATLAS: User/custom rects where user code wants to store UV coordinates will need to do the same thing. @@ -3610,7 +3627,7 @@ static void ImFontAtlasBuildUpdateBasicTexData(ImFontAtlas* atlas) atlas->TexUvWhitePixel = ImVec2((r.x + 0.5f) * atlas->TexUvScale.x, (r.y + 0.5f) * atlas->TexUvScale.y); } -static void ImFontAtlasBuildUpdateLinesTexData(ImFontAtlas* atlas) +static void ImFontAtlasBuildUpdateTexDataLines(ImFontAtlas* atlas) { if (atlas->Flags & ImFontAtlasFlags_NoBakedLines) return; @@ -4059,6 +4076,12 @@ static void ImFontAtlasBuildSetTexture(ImFontAtlas* atlas, ImTextureData* tex) ImFontAtlasUpdateDrawListsTextures(atlas, old_tex_ref, atlas->TexRef); } +static void ImFontAtlasBuildUpdateTexData(ImFontAtlas* atlas) +{ + ImFontAtlasBuildUpdateTexDataBasic(atlas); + ImFontAtlasBuildUpdateTexDataLines(atlas); +} + // Create a new texture, discard previous one ImTextureData* ImFontAtlasTextureAdd(ImFontAtlas* atlas, int w, int h) { @@ -4173,8 +4196,7 @@ void ImFontAtlasTextureRepack(ImFontAtlas* atlas, int w, int h) } // Update other cached UV - ImFontAtlasBuildUpdateLinesTexData(atlas); - ImFontAtlasBuildUpdateBasicTexData(atlas); + ImFontAtlasBuildUpdateTexData(atlas); builder->LockDisableResize = false; ImFontAtlasUpdateDrawListsSharedData(atlas); @@ -4323,8 +4345,7 @@ void ImFontAtlasBuildInit(ImFontAtlas* atlas) ImFontAtlasPackInit(atlas); // Add required texture data - ImFontAtlasBuildUpdateLinesTexData(atlas); - ImFontAtlasBuildUpdateBasicTexData(atlas); + ImFontAtlasBuildUpdateTexData(atlas); // Register fonts ImFontAtlasBuildUpdatePointers(atlas); @@ -4353,6 +4374,8 @@ void ImFontAtlasBuildDestroy(ImFontAtlas* atlas) atlas->Builder = NULL; } +//----------------------------------------------------------------------------- + void ImFontAtlasPackInit(ImFontAtlas * atlas) { ImTextureData* tex = atlas->TexData; @@ -5651,6 +5674,7 @@ ImVec2 ImFontCalcTextSizeEx(ImFont* font, float size, float max_width, float wra text_end_display = text_end; ImFontBaked* baked = font->GetFontBaked(size); + // [PCSX2 Custom] const float line_height = ImCeil(size * font->LineHeight); const float scale = size / baked->Size; @@ -5749,8 +5773,13 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, Im if (glyph->Colored) col |= ~IM_COL32_A_MASK; float scale = (size >= 0.0f) ? (size / baked->Size) : 1.0f; - float x = IM_TRUNC(pos.x); - float y = IM_TRUNC(pos.y); + float x = pos.x; + float y = pos.y; + if ((draw_list->Flags & ImDrawListFlags_TextNoPixelSnap) == 0) + { + x = IM_TRUNC(x); + y = IM_TRUNC(y); + } float x1 = x + glyph->X0 * scale; float x2 = x + glyph->X1 * scale; @@ -5782,17 +5811,22 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, Im // DO NOT CALL DIRECTLY THIS WILL CHANGE WILDLY IN 2026. Use ImDrawList::AddText(). void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, ImDrawTextFlags flags) { - // Align to be pixel perfect begin: - float x = IM_TRUNC(pos.x); - float y = IM_TRUNC(pos.y); + // Align to be pixel perfect + float x = pos.x; + float y = pos.y; if (y > clip_rect.w) return; + if ((draw_list->Flags & ImDrawListFlags_TextNoPixelSnap) == 0) + { + x = IM_TRUNC(x); + y = IM_TRUNC(y); + } if (!text_end) text_end = text_begin + ImStrlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls. - const float line_height = ImCeil(size * LineHeight); + const float line_height = ImCeil(size * LineHeight); // [PCSX2 Custom] ImFontBaked* baked = GetFontBaked(size); const float scale = size / baked->Size; @@ -6173,8 +6207,8 @@ void ImGui::RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p flags = ImDrawFlags_RoundCornersDefault_; if (((col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) < 0xFF) { - ImU32 col_bg1 = GetColorU32(ImAlphaBlendColors(IM_COL32(204, 204, 204, 255), col)); - ImU32 col_bg2 = GetColorU32(ImAlphaBlendColors(IM_COL32(128, 128, 128, 255), col)); + ImU32 col_bg1 = GetColorU32(ImAlphaBlendColors(IM_COL32(128, 128, 128, 255), col)); + ImU32 col_bg2 = GetColorU32(ImAlphaBlendColors(IM_COL32(204, 204, 204, 255), col)); draw_list->AddRectFilled(p_min, p_max, col_bg1, rounding, flags); int yi = 0; @@ -6183,12 +6217,12 @@ void ImGui::RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p float y1 = ImClamp(y, p_min.y, p_max.y), y2 = ImMin(y + grid_step, p_max.y); if (y2 <= y1) continue; - for (float x = p_min.x + grid_off.x + (yi & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f) + for (float x = p_min.x + grid_off.x + ((yi ^ 1) & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f) { float x1 = ImClamp(x, p_min.x, p_max.x), x2 = ImMin(x + grid_step, p_max.x); if (x2 <= x1) continue; - ImDrawFlags cell_flags = ImDrawFlags_RoundCornersNone; + ImDrawFlags cell_flags = ImDrawFlags_RoundCornersNone; // FIXME: Could use CalcRoundingFlagsForRectInRect() if (y1 <= p_min.y) { if (x1 <= p_min.x) cell_flags |= ImDrawFlags_RoundCornersTopLeft; if (x2 >= p_max.x) cell_flags |= ImDrawFlags_RoundCornersTopRight; } if (y2 >= p_max.y) { if (x1 <= p_min.x) cell_flags |= ImDrawFlags_RoundCornersBottomLeft; if (x2 >= p_max.x) cell_flags |= ImDrawFlags_RoundCornersBottomRight; } @@ -6330,7 +6364,7 @@ static unsigned int stb_decompress(unsigned char *output, const unsigned char *i // Download and more information at https://github.com/bluescan/proggyfonts //----------------------------------------------------------------------------- -#ifndef IMGUI_DISABLE_DEFAULT_FONT +#if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_BITMAP) // File: 'ProggyClean.ttf' (41208 bytes) // Exported using binary_to_compressed_c.exe -u8 "ProggyClean.ttf" proggy_clean_ttf @@ -6505,11 +6539,12 @@ static const unsigned char proggy_clean_ttf_compressed_data[9583] = 239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,35,57,102,0,0,5,250,72,249,98,247, }; -static const char* GetDefaultCompressedFontDataProggyClean(int* out_size) +const char* ImGui_GetDefaultCompressedFontDataProggyClean(int* out_size) { *out_size = proggy_clean_ttf_compressed_size; return (const char*)proggy_clean_ttf_compressed_data; } +#endif // #if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_BITMAP) //----------------------------------------------------------------------------- // [SECTION] Default font data (ProggyForever-Regular-minimal.ttf) @@ -6518,6 +6553,8 @@ static const char* GetDefaultCompressedFontDataProggyClean(int* out_size) // MIT license / Copyright (c) 2026 Disco Hello, Copyright (c) 2019,2023 Tristan Grimmer //----------------------------------------------------------------------------- +#if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_VECTOR) + // File: 'output/ProggyForever-Regular-minimal.ttf' (18556 bytes) // Exported using binary_to_compressed_c.exe -u8 "output/ProggyForever-Regular-minimal.ttf" proggy_forever_minimal_ttf static const unsigned int proggy_forever_minimal_ttf_compressed_size = 14562; @@ -6767,12 +6804,11 @@ static const unsigned char proggy_forever_minimal_ttf_compressed_data[14562] = 3,36,0,229,13,183,147,132,7,42,175,187,66,0,0,0,0,229,178,59,232,5,250,48,120,202,241, }; -static const char* GetDefaultCompressedFontDataProggyForever(int* out_size) +const char* ImGui_GetDefaultCompressedFontDataProggyForever(int* out_size) { *out_size = proggy_forever_minimal_ttf_compressed_size; return (const char*)proggy_forever_minimal_ttf_compressed_data; } - -#endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT +#endif // #if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_VECTOR) #endif // #ifndef IMGUI_DISABLE diff --git a/3rdparty/imgui/src/imgui_freetype.cpp b/3rdparty/imgui/src/imgui_freetype.cpp index 9370f6df29..f21c5a1ddf 100644 --- a/3rdparty/imgui/src/imgui_freetype.cpp +++ b/3rdparty/imgui/src/imgui_freetype.cpp @@ -450,7 +450,7 @@ static bool ImGui_ImplFreeType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* s // (FT_Set_Pixel_Sizes() essentially calls FT_Request_Size() with FT_SIZE_REQUEST_TYPE_NOMINAL) const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity; FT_Size_RequestRec req; - req.type = FT_SIZE_REQUEST_TYPE_NOMINAL; + req.type = FT_SIZE_REQUEST_TYPE_NOMINAL; // [PCSX2 Custom] req.width = 0; req.height = (uint32_t)(size * 64 * rasterizer_density); req.horiResolution = 0; diff --git a/3rdparty/imgui/src/imgui_tables.cpp b/3rdparty/imgui/src/imgui_tables.cpp index 135410a0dd..f8fab3778b 100644 --- a/3rdparty/imgui/src/imgui_tables.cpp +++ b/3rdparty/imgui/src/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 +// dear imgui, v1.92.9b // (tables and columns code) /* @@ -40,13 +40,15 @@ Index of this file: // | TableBeginInitMemory() - first time table is used // | TableResetSettings() - on settings reset // | TableLoadSettings() - on settings load -// | TableBeginApplyRequests() - apply queued resizing/reordering/hiding requests -// | - TableSetColumnWidth() - apply resizing width (for mouse resize, often requested by previous frame) -// | - TableUpdateColumnsWeightFromWidth()- recompute columns weights (of stretch columns) from their respective width +//----------------------------------------------------------------------------- // - TableSetupColumn() user submit columns details (optional) // - TableSetupScrollFreeze() user submit scroll freeze information (optional) //----------------------------------------------------------------------------- // - TableUpdateLayout() [Internal] followup to BeginTable(): setup everything: widths, columns positions, clipping rectangles. Automatically called by the FIRST call to TableNextRow() or TableHeadersRow(). +// | TableApplyQueuedRequests() - apply queued resizing/reordering/hiding requests +// | - TableSetColumnWidth() - apply resizing width (for mouse resize, often requested by previous frame) +// | - TableUpdateColumnsWeightFromWidth()- recompute columns weights (of stretch columns) from their respective width +// | - TableSetColumnDisplayOrder() - apply reordering a column // | TableSetupDrawChannels() - setup ImDrawList channels // | TableUpdateBorders() - detect hovering columns for resize, ahead of contents submission // | TableBeginContextMenuPopup() @@ -252,7 +254,7 @@ Index of this file: // - BeginTable() // - BeginTableEx() [Internal] // - TableBeginInitMemory() [Internal] -// - TableBeginApplyRequests() [Internal] +// - TableApplyQueuedRequests() [Internal] // - TableSetupColumnFlags() [Internal] // - TableUpdateLayout() [Internal] // - TableUpdateBorders() [Internal] @@ -361,6 +363,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG g.TablesTempData.resize(g.TablesTempDataStacked, ImGuiTableTempData()); ImGuiTableTempData* temp_data = table->TempData = &g.TablesTempData[g.TablesTempDataStacked - 1]; temp_data->TableIndex = table_idx; + temp_data->ReconcileColumnsRequests.resize(0); // FIXME: Use shrink(0) everywhere? table->DrawSplitter = &table->TempData->DrawSplitter; table->DrawSplitter->Clear(); @@ -372,6 +375,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG const int previous_frame_active = table->LastFrameActive; const int instance_no = (previous_frame_active != g.FrameCount) ? 0 : table->InstanceCurrent + 1; const ImGuiTableFlags previous_flags = table->Flags; + const bool is_new_table = (previous_frame_active == -1); table->ID = id; table->Flags = flags; table->LastFrameActive = g.FrameCount; @@ -486,17 +490,18 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG // FIXME: Because inner_window's Scrollbar doesn't know about border size, since it's not encoded in window->WindowBorderSize, // it already overlaps it and doesn't need an extra offset. Ideally we should be able to pass custom border size with // different x/y values to BeginChild(). + const float border_size = TABLE_BORDER_SIZE; if (flags & ImGuiTableFlags_BordersOuterV) { - table->HostClipRect.Min.x = ImMin(table->HostClipRect.Min.x + TABLE_BORDER_SIZE, table->HostClipRect.Max.x); + table->HostClipRect.Min.x = ImMin(table->HostClipRect.Min.x + border_size, table->HostClipRect.Max.x); if (inner_window->DecoOuterSizeX2 == 0.0f) - table->HostClipRect.Max.x = ImMax(table->HostClipRect.Max.x - TABLE_BORDER_SIZE, table->HostClipRect.Min.x); + table->HostClipRect.Max.x = ImMax(table->HostClipRect.Max.x - border_size, table->HostClipRect.Min.x); } if (flags & ImGuiTableFlags_BordersOuterH) { - table->HostClipRect.Min.y = ImMin(table->HostClipRect.Min.y + TABLE_BORDER_SIZE, table->HostClipRect.Max.y); + table->HostClipRect.Min.y = ImMin(table->HostClipRect.Min.y + border_size, table->HostClipRect.Max.y); if (inner_window->DecoOuterSizeY2 == 0.0f) - table->HostClipRect.Max.y = ImMax(table->HostClipRect.Max.y - TABLE_BORDER_SIZE, table->HostClipRect.Min.y); + table->HostClipRect.Max.y = ImMax(table->HostClipRect.Max.y - border_size, table->HostClipRect.Min.y); } } @@ -561,29 +566,36 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->MemoryCompacted = false; // Setup memory buffer (clear data if columns count changed) - 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) { - // Attempt to preserve width and other settings on column count/specs change (#4046) - old_columns_to_preserve = table->Columns.Data; - old_columns_raw_data = table->RawData; // Free at end of function + // Attempt to preserve width and other settings on column count/specs change (#4046, #9108) + IMGUI_DEBUG_LOG_TABLE("[table] Table 0x%08X column count %d -> %d, recreating storage.\n", table->ID, old_columns_count, columns_count); + IM_ASSERT(temp_data->OldColumnsRawData == NULL); + temp_data->OldColumnsRawData = table->RawData; // Freed during layout + temp_data->OldColumnsData = table->Columns; + for (ImGuiTableColumn& src_column : table->TempData->OldColumnsData) + src_column.IsNeedReconcileSrc = true; table->RawData = NULL; } if (table->RawData == NULL) { TableBeginInitMemory(table, columns_count); - table->IsInitializing = table->IsSettingsRequestLoad = true; + table->IsInitializing = true; } if (table->IsResetAllRequest) TableResetSettings(table); if (table->IsInitializing) { // Initialize - table->SettingsOffset = -1; + if (is_new_table) + { + table->SettingsOffset = -1; + table->IsSettingsRequestLoad = true; + } table->IsSortSpecsDirty = true; table->IsSettingsDirty = true; // Records itself into .ini file even when in default state (#7934) + table->IsReconcileMode = false; table->InstanceInteracted = -1; table->ContextPopupColumn = -1; table->ReorderColumn = table->ReorderColumnDstOrder = table->ResizedColumn = table->LastResizedColumn = -1; @@ -592,9 +604,9 @@ 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]; - if (old_columns_to_preserve && n < old_columns_count) + if (temp_data->OldColumnsData.Data && n < temp_data->OldColumnsData.size()) { - *column = old_columns_to_preserve[n]; + *column = temp_data->OldColumnsData[n]; } else { @@ -608,28 +620,11 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->DisplayOrderToIndex[n] = column->DisplayOrder; } } - if (old_columns_raw_data) - IM_FREE(old_columns_raw_data); // Load settings if (table->IsSettingsRequestLoad) TableLoadSettings(table); - // Handle DPI/font resize - // This is designed to facilitate DPI changes with the assumption that e.g. style.CellPadding has been scaled as well. - // It will also react to changing fonts with mixed results. It doesn't need to be perfect but merely provide a decent transition. - // FIXME-DPI: Provide consistent standards for reference size. Perhaps using g.CurrentDpiScale would be more self explanatory. - // This is will lead us to non-rounded WidthRequest in columns, which should work but is a poorly tested path. - const float new_ref_scale_unit = g.FontSize; // g.Font->GetCharAdvance('A') ? - if (table->RefScale != 0.0f && table->RefScale != new_ref_scale_unit) - { - const float scale_factor = new_ref_scale_unit / table->RefScale; - //IMGUI_DEBUG_PRINT("[table] %08X RefScaleUnit %.3f -> %.3f, scaling width by %.3f\n", table->ID, table->RefScaleUnit, new_ref_scale_unit, scale_factor); - for (int n = 0; n < columns_count; n++) - table->Columns[n].WidthRequest = table->Columns[n].WidthRequest * scale_factor; - } - table->RefScale = new_ref_scale_unit; - // Disable output until user calls TableNextRow() or TableNextColumn() leading to the TableUpdateLayout() call.. // This is not strictly necessary but will reduce cases were "out of table" output will be misleading to the user. // Because we cannot safely assert in EndTable() when no rows have been created, this seems like our best option. @@ -639,10 +634,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG // At this point the ->NameOffset field of each column will be invalid until TableUpdateLayout() or the first call to TableSetupColumn() if (table->ColumnsNames.Buf.Size > 0) table->ColumnsNames.Buf.resize(0); - - // Apply queued resizing/reordering/hiding requests - TableBeginApplyRequests(table); - + return true; } @@ -677,7 +669,7 @@ void ImGui::TableBeginInitMemory(ImGuiTable* table, int columns_count) } // Apply queued resizing/reordering/hiding requests -void ImGui::TableBeginApplyRequests(ImGuiTable* table) +void ImGui::TableApplyQueuedRequests(ImGuiTable* table) { // Handle resizing request // (We process this in the TableBegin() of the first instance of each table) @@ -719,7 +711,7 @@ void ImGui::TableBeginApplyRequests(ImGuiTable* table) // table->ReorderColumn = -1; } - // Handle display order reset request + // Handle display order / visibility reset requests if (table->IsResetDisplayOrderRequest) { for (int n = 0; n < table->ColumnsCount; n++) @@ -727,6 +719,13 @@ void ImGui::TableBeginApplyRequests(ImGuiTable* table) table->IsResetDisplayOrderRequest = false; table->IsSettingsDirty = true; } + if (table->IsResetVisibilityRequest) + { + for (ImGuiTableColumn& column : table->Columns) + column.IsUserEnabled = column.IsUserEnabledNextFrame = (column.Flags & ImGuiTableColumnFlags_DefaultHide) ? 0 : 1; + table->IsResetVisibilityRequest = false; + table->IsSettingsDirty = true; + } } // Apply immediately. See TableQueueSetColumnDisplayOrder() for additional checks/constraints. @@ -782,8 +781,9 @@ void ImGui::TableQueueSetColumnDisplayOrder(ImGuiTable* table, int column_n, int table->ReorderColumn = (ImGuiTableColumnIdx)column_n; table->ReorderColumnDstOrder = (ImGuiTableColumnIdx)-1; dst_order = TableGetMaxDisplayOrderAllowed(table, src_order, dst_order); - if (dst_order != src_order) - table->ReorderColumnDstOrder = (ImGuiTableColumnIdx)dst_order; + if (table->IsLayoutLocked && dst_order == src_order) // We allow calling the function before layout w/ reconcile so don't early out. + return; + table->ReorderColumnDstOrder = (ImGuiTableColumnIdx)dst_order; } // Adjust flags: default width mode + stretch columns are not allowed when auto extending @@ -849,13 +849,61 @@ static void TableSetupColumnFlags(ImGuiTable* table, ImGuiTableColumn* column, I void ImGui::TableUpdateLayout(ImGuiTable* table) { ImGuiContext& g = *GImGui; + ImGuiTableTempData* temp_data = table->TempData; IM_ASSERT(table->IsLayoutLocked == false); + const int columns_count = table->ColumnsCount; + + // Reconcile moved columns + if (temp_data->ReconcileColumnsRequests.Size > 0) + TableReconcileColumns(table); + if (temp_data->OldColumnsRawData) + { + IM_FREE(temp_data->OldColumnsRawData); + temp_data->OldColumnsRawData = NULL; + temp_data->OldColumnsData.clear(); + } + + // Apply columns settings + if (table->IsSettingsRequestLoad) + TableLoadSettingsForColumns(table); + if (table->IsInitializing || table->IsSettingsRequestLoad) + { + for (ImGuiTableColumn& column : table->Columns) + { + ImGuiTableFlags init_flags; + if (table->IsSettingsRequestLoad) + init_flags = column.IsLoadedSettings ? ~table->SettingsLoadedFlags : ~0; + else + init_flags = column.IsJustCreated ? ~0 : 0; + TableInitColumnDefaults(table, &column, init_flags); + } + TableFixDisplayOrder(table); // Call even for non _Reorderable table as we loaded .ini data. + table->IsSettingsRequestLoad = false; + } + + // Apply queued resizing/reordering/hiding requests + TableApplyQueuedRequests(table); + + // Handle DPI/font resize + // This is designed to facilitate DPI changes with the assumption that e.g. style.CellPadding has been scaled as well. + // It will also react to changing fonts with mixed results. It doesn't need to be perfect but merely provide a decent transition. + // FIXME-DPI: Provide consistent standards for reference size. Perhaps using g.CurrentDpiScale would be more self explanatory. + // This is will lead us to non-rounded WidthRequest in columns, which should work but is a poorly tested path. + const float new_ref_scale_unit = g.FontSize; // g.Font->GetCharAdvance('A') ? + if (table->RefScale != 0.0f && table->RefScale != new_ref_scale_unit) + { + const float scale_factor = new_ref_scale_unit / table->RefScale; + IMGUI_DEBUG_LOG_TABLE("[table] 0x%08X RefScale %.3f -> %.3f, scaling width by %.3f\n", table->ID, table->RefScale, new_ref_scale_unit, scale_factor); + for (int n = 0; n < columns_count; n++) + table->Columns[n].WidthRequest = table->Columns[n].WidthRequest * scale_factor; + } + table->RefScale = new_ref_scale_unit; const ImGuiTableFlags table_sizing_policy = (table->Flags & ImGuiTableFlags_SizingMask_); - table->IsDefaultDisplayOrder = true; + table->IsDefaultDisplayOrder = table->IsDefaultVisibility = true; table->ColumnsEnabledCount = 0; - ImBitArrayClearAllBits(table->EnabledMaskByIndex, table->ColumnsCount); - ImBitArrayClearAllBits(table->EnabledMaskByDisplayOrder, table->ColumnsCount); + ImBitArrayClearAllBits(table->EnabledMaskByIndex, columns_count); + ImBitArrayClearAllBits(table->EnabledMaskByDisplayOrder, columns_count); table->LeftMostEnabledColumn = -1; table->MinColumnWidth = ImMax(1.0f, g.Style.FramePadding.x * 1.0f); // g.Style.ColumnsMinSpacing; // FIXME-TABLE @@ -868,11 +916,9 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) bool has_resizable = false; float stretch_sum_width_auto = 0.0f; float fixed_max_width_auto = 0.0f; - for (int order_n = 0; order_n < table->ColumnsCount; order_n++) + for (int order_n = 0; order_n < columns_count; order_n++) { const int column_n = table->DisplayOrderToIndex[order_n]; - if (column_n != order_n) - table->IsDefaultDisplayOrder = false; ImGuiTableColumn* column = &table->Columns[column_n]; // Clear column setup if not submitted by user. Currently we make it mandatory to call TableSetupColumn() every frame. @@ -882,7 +928,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) { TableSetupColumnFlags(table, column, ImGuiTableColumnFlags_None); column->NameOffset = -1; - column->UserID = 0; + column->ID = column->UserData = 0; column->InitStretchWeightOrWidth = -1.0f; } @@ -895,6 +941,12 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) table->IsSettingsDirty = true; } column->IsEnabled = column->IsUserEnabled && (column->Flags & ImGuiTableColumnFlags_Disabled) == 0; + column->IsJustCreated = false; + + if (column->IsEnabled != ((column->Flags & ImGuiTableColumnFlags_DefaultHide) ? 0 : 1)) + table->IsDefaultVisibility = false; + if (column_n != order_n) + table->IsDefaultDisplayOrder = false; if (column->SortOrder != -1 && !column->IsEnabled) table->IsSortSpecsDirty = true; @@ -967,7 +1019,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) float sum_width_requests = 0.0f; // Sum of all width for fixed and auto-resize columns, excluding width contributed by Stretch columns but including spacing/padding. float stretch_sum_weights = 0.0f; // Sum of all weights for stretch columns. table->LeftMostStretchedColumn = table->RightMostStretchedColumn = -1; - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + for (int column_n = 0; column_n < columns_count; column_n++) { if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n)) continue; @@ -1031,7 +1083,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) const float width_avail_for_stretched_columns = width_avail - width_spacings - sum_width_requests; float width_remaining_for_stretched_columns = width_avail_for_stretched_columns; table->ColumnsGivenWidth = width_spacings + (table->CellPaddingX * 2.0f) * table->ColumnsEnabledCount; - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + for (int column_n = 0; column_n < columns_count; column_n++) { if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n)) continue; @@ -1058,7 +1110,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // [Part 5] Redistribute stretch remainder width due to rounding (remainder width is < 1.0f * number of Stretch column). // Using right-to-left distribution (more likely to match resizing cursor). if (width_remaining_for_stretched_columns >= 1.0f && !(table->Flags & ImGuiTableFlags_PreciseWidths)) - for (int order_n = table->ColumnsCount - 1; stretch_sum_weights > 0.0f && width_remaining_for_stretched_columns >= 1.0f && order_n >= 0; order_n--) + for (int order_n = columns_count - 1; stretch_sum_weights > 0.0f && width_remaining_for_stretched_columns >= 1.0f && order_n >= 0; order_n--) { if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n)) continue; @@ -1098,8 +1150,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) float offset_x = ((table->FreezeColumnsCount > 0) ? table->OuterRect.Min.x : work_rect.Min.x) + table->OuterPaddingX - table->CellSpacingX1; ImRect host_clip_rect = table->InnerClipRect; //host_clip_rect.Max.x += table->CellPaddingX + table->CellSpacingX2; - ImBitArrayClearAllBits(table->VisibleMaskByIndex, table->ColumnsCount); - for (int order_n = 0; order_n < table->ColumnsCount; order_n++) + ImBitArrayClearAllBits(table->VisibleMaskByIndex, columns_count); + for (int order_n = 0; order_n < columns_count; order_n++) { const int column_n = table->DisplayOrderToIndex[order_n]; ImGuiTableColumn* column = &table->Columns[column_n]; @@ -1247,7 +1299,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) const float unused_x1 = ImMax(table->WorkRect.Min.x, table->Columns[table->RightMostEnabledColumn].ClipRect.Max.x); if (is_hovering_table && table->HoveredColumnBody == -1) if (mouse_skewed_x >= unused_x1) - table->HoveredColumnBody = (ImGuiTableColumnIdx)table->ColumnsCount; + table->HoveredColumnBody = (ImGuiTableColumnIdx)columns_count; if (has_resizable == false && (table->Flags & ImGuiTableFlags_Resizable)) table->Flags &= ~ImGuiTableFlags_Resizable; @@ -1289,7 +1341,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) table->HighlightColumnHeader = -1; if (table->IsContextPopupOpen && table->ContextPopupColumn != -1 && table->InstanceInteracted == table->InstanceCurrent) table->HighlightColumnHeader = table->ContextPopupColumn; - else if ((table->Flags & ImGuiTableFlags_HighlightHoveredColumn) && table->HoveredColumnBody != -1 && table->HoveredColumnBody != table->ColumnsCount && table->HoveredColumnBorder == -1) + else if ((table->Flags & ImGuiTableFlags_HighlightHoveredColumn) && table->HoveredColumnBody != -1 && table->HoveredColumnBody != columns_count && table->HoveredColumnBorder == -1) if (g.ActiveId == 0 || (table->IsActiveIdInTable || g.DragDropActive)) table->HighlightColumnHeader = table->HoveredColumnBody; @@ -1629,9 +1681,9 @@ void ImGui::EndTable() NavUpdateCurrentWindowIsScrollPushableX(); } -// Called in TableSetupColumn() when initializing and in TableLoadSettings() for defaults before applying stored settings. +// Called in TableUpdateLayout() when initializing/after loading settings. // 'init_mask' specify which fields to initialize. -static void TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags init_mask) +void ImGui::TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags init_mask) { ImGuiTableColumnFlags flags = column->Flags; if (init_mask & ImGuiTableFlags_Resizable) @@ -1643,7 +1695,7 @@ static void TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column, column->AutoFitQueue = 0x00; } if (init_mask & ImGuiTableFlags_Reorderable) - column->DisplayOrder = (ImGuiTableColumnIdx)table->Columns.index_from_ptr(column); + column->DisplayOrder = (ImGuiTableColumnIdx)((table->Flags & ImGuiTableFlags_Reorderable) ? -1 : table->Columns.index_from_ptr(column)); if (init_mask & ImGuiTableFlags_Hideable) column->IsUserEnabled = column->IsUserEnabledNextFrame = (flags & ImGuiTableColumnFlags_DefaultHide) ? 0 : 1; if (init_mask & ImGuiTableFlags_Sortable) @@ -1656,17 +1708,9 @@ static void TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column, // See "COLUMNS SIZING POLICIES" comments at the top of this file // If (init_width_or_weight <= 0.0f) it is ignored -void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_id) +static void TableSetupColumnApply(ImGuiTable* table, int idx, ImGuiID id, ImS16 name_offset, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_data) { - ImGuiContext& g = *GImGui; - ImGuiTable* table = g.CurrentTable; - IM_ASSERT_USER_ERROR_RET(table != NULL, "Call should only be done while in BeginTable() scope!"); - IM_ASSERT_USER_ERROR_RET(table->DeclColumnsCount < table->ColumnsCount, "TableSetupColumn(): called too many times!"); - IM_ASSERT_USER_ERROR_RET(table->IsLayoutLocked == false, "TableSetupColumn(): need to call before first row!"); // Table layout is locked when submitting a row or when calling BeginMultiSelect() with box-select. - IM_ASSERT((flags & ImGuiTableColumnFlags_StatusMask_) == 0 && "Illegal to pass StatusMask values to TableSetupColumn()"); - - ImGuiTableColumn* column = &table->Columns[table->DeclColumnsCount]; - table->DeclColumnsCount++; + ImGuiTableColumn* column = &table->Columns[idx]; // Assert when passing a width or weight if policy is entirely left to default, to avoid storing width into weight and vice-versa. // Give a grace to users of ImGuiTableFlags_ScrollX. @@ -1685,27 +1729,127 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo } TableSetupColumnFlags(table, column, flags); - column->UserID = user_id; - flags = column->Flags; - - // Initialize defaults + column->ID = id; + column->UserData = user_data; + column->NameOffset = name_offset; column->InitStretchWeightOrWidth = init_width_or_weight; - if (table->IsInitializing) - { - ImGuiTableFlags init_flags = ~table->SettingsLoadedFlags; - if (column->WidthRequest < 0.0f && column->StretchWeight < 0.0f) - init_flags |= ImGuiTableFlags_Resizable; - TableInitColumnDefaults(table, column, init_flags); - } +} + +void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_data) +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + IM_ASSERT_USER_ERROR_RET(table != NULL, "Call should only be done while in BeginTable() scope!"); + IM_ASSERT_USER_ERROR_RET(table->DeclColumnsCount < table->ColumnsCount, "TableSetupColumn(): called too many times!"); + IM_ASSERT_USER_ERROR_RET(table->IsLayoutLocked == false, "TableSetupColumn(): need to call before first row!"); // Table layout is locked when submitting a row or when calling BeginMultiSelect() with box-select. + IM_ASSERT((flags & ImGuiTableColumnFlags_StatusMask_) == 0 && "Illegal to pass StatusMask values to TableSetupColumn()"); // Store name (append with zero-terminator in contiguous buffer) // FIXME: If we recorded the number of \n in names we could compute header row height - column->NameOffset = -1; + ImS16 name_offset = -1; if (label != NULL && label[0] != 0) { - column->NameOffset = (ImS16)table->ColumnsNames.size(); + name_offset = (ImS16)table->ColumnsNames.size(); table->ColumnsNames.append(label, label + ImStrlen(label) + 1); } + const ImGuiID column_id = (label != NULL && label[0] != 0) ? ImHashStr(label) : 0; + + // When ID changed or a column moved: defer the request until layout where we will process full reconcile. + const int column_idx = table->DeclColumnsCount++; + ImGuiTableColumn* column = &table->Columns[column_idx]; + + // If topology change goes into reconcile mode + if (table->IsReconcileMode == false && column->ID != column_id) + { + table->IsReconcileMode = true; + table->TempData->ReconcileColumnsRequests.reserve(table->ColumnsCount - column_idx); + } + + // Fast/common path + if (table->IsReconcileMode == false) + { + TableSetupColumnApply(table, column_idx, column_id, name_offset, flags, init_width_or_weight, user_data); + column->IsNeedReconcileSrc = column->IsNeedReconcileDst = false; + if (column_idx < table->TempData->OldColumnsData.size()) + table->TempData->OldColumnsData[column_idx].IsNeedReconcileSrc = false; + return; + } + + // Reconcile path: defer applying data to TableUpdateLayout() -> TableReconcileMovedColumns() -> TableSetupColumnApply(). + // On column count change: search the previous columns: + // - the live Columns[] array is truncated on shrink, so a moved column may only exist in old data. + // - the live Columns[] array may already be partially reset. + table->TempData->ReconcileColumnsRequests.push_back(ImGuiTableReconcileColumnData()); + ImGuiTableReconcileColumnData& reconcile_data = table->TempData->ReconcileColumnsRequests.back(); + reconcile_data.ID = column_id; + reconcile_data.NameOffset = name_offset; + reconcile_data.Flags = flags; + reconcile_data.InitWidthOrWeight = init_width_or_weight; + reconcile_data.UserData = user_data; + reconcile_data.ColumnNewIdx = (ImGuiTableColumnIdx)column_idx; + reconcile_data.ColumnOldIdx = (ImGuiTableColumnIdx)-1; + column->NameOffset = name_offset; // Allow TableGetColumnName() to work before layout + column->IsNeedReconcileSrc = column->IsNeedReconcileDst = true; +} + +// NB: This was written to be similar to the logic in TableLoadSettingsForColumns(). +void ImGui::TableReconcileColumns(ImGuiTable* table) +{ + ImGuiContext& g = *GImGui; + ImGuiTableTempData* temp_data = table->TempData; + IM_UNUSED(g); + IMGUI_DEBUG_LOG_TABLE("[table] Reconcile columns for table 0x%08X\n", table->ID); + + ImSpan& dst_columns = table->Columns; + ImSpan& src_columns = table->TempData->OldColumnsData.empty() ? table->Columns : table->TempData->OldColumnsData; + + // Find matches for named columns. + int matches = 0; + ImVector& reconcile_requests = temp_data->ReconcileColumnsRequests; + for (ImGuiTableReconcileColumnData& reconcile_data : reconcile_requests) + if (reconcile_data.ID != 0) + for (ImGuiTableColumn& src_column : src_columns) + if (src_column.ID == reconcile_data.ID && src_column.IsNeedReconcileSrc) + { + ImGuiTableColumn& dst_column = dst_columns[reconcile_data.ColumnNewIdx]; + IM_ASSERT(src_column.IsNeedReconcileSrc && dst_column.IsNeedReconcileDst); + src_column.IsNeedReconcileSrc = dst_column.IsNeedReconcileDst = false; + reconcile_data.ColumnOldIdx = (ImGuiTableColumnIdx)src_columns.index_from_ptr(&src_column); + reconcile_data.ColumnOldData = src_column; + matches++; + break; + } + + // Remaining entries are matched sequentially. + int dst_idx = 0; // index in reconcile array + if (matches != reconcile_requests.Size) + for (ImGuiTableColumn& src_column : src_columns) + { + if (!src_column.IsNeedReconcileSrc) + continue; + while (dst_idx < reconcile_requests.Size && reconcile_requests[dst_idx].ColumnOldIdx != (ImGuiTableColumnIdx)-1) + dst_idx++; + if (dst_idx == reconcile_requests.Size) + break; + ImGuiTableReconcileColumnData& reconcile_data = reconcile_requests[dst_idx]; + ImGuiTableColumn& dst_column = dst_columns[reconcile_data.ColumnNewIdx]; + IM_ASSERT(src_column.IsNeedReconcileSrc && dst_column.IsNeedReconcileDst); + src_column.IsNeedReconcileSrc = dst_column.IsNeedReconcileDst = false; + reconcile_data.ColumnOldIdx = (ImGuiTableColumnIdx)src_columns.index_from_ptr(&src_column); + reconcile_data.ColumnOldData = src_column; + } + + // Apply in the final pass. Because it is possible that src_columns == table->Columns we went through a temporary copy. + for (ImGuiTableReconcileColumnData& reconcile_data : reconcile_requests) + { + table->Columns[reconcile_data.ColumnNewIdx] = reconcile_data.ColumnOldData; // When old column was not found clear anyway with default-constructed data (which will set IsJustCreated=true) + TableSetupColumnApply(table, reconcile_data.ColumnNewIdx, reconcile_data.ID, reconcile_data.NameOffset, reconcile_data.Flags, reconcile_data.InitWidthOrWeight, reconcile_data.UserData); + IMGUI_DEBUG_LOG_TABLE("[table] - old %d -> new %d \"%s\"\n", reconcile_data.ColumnOldIdx, reconcile_data.ColumnNewIdx, TableGetColumnName(table, reconcile_data.ColumnNewIdx)); // Log at the end so NameOffset was copied. + } + TableFixDisplayOrder(table); + table->IsSettingsDirty = true; // FIXME-RECONCILE: Necessary? + table->IsReconcileMode = false; + reconcile_requests.resize(0); // GC-ed once in NewFrame() } // [Public] @@ -2882,7 +3026,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) else if (table->Flags & ImGuiTableFlags_BordersOuterH) { inner_drawlist->AddLineH(outer_border.Min.x, outer_border.Max.x, outer_border.Min.y, outer_col, border_size); - inner_drawlist->AddLineH(outer_border.Min.x, outer_border.Max.x, outer_border.Max.y, outer_col, border_size); + inner_drawlist->AddLineH(outer_border.Min.x, outer_border.Max.x, outer_border.Max.y - border_size, outer_col, border_size); } } if ((table->Flags & ImGuiTableFlags_BordersInnerH) && table->RowPosY2 < table->OuterRect.Max.y) @@ -3081,7 +3225,7 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table) continue; IM_ASSERT(column->SortOrder < table->SortSpecsCount); ImGuiTableColumnSortSpecs* sort_spec = &sort_specs[column->SortOrder]; - sort_spec->ColumnUserID = column->UserID; + sort_spec->ColumnUserID = column->UserData; sort_spec->ColumnIndex = (ImGuiTableColumnIdx)column_n; sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder; sort_spec->SortDirection = (ImGuiSortDirection)column->SortDirection; @@ -3252,7 +3396,7 @@ void ImGui::TableHeader(const char* label) if ((table->RowFlags & ImGuiTableRowFlags_Headers) == 0) TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_TableHeaderBg), table->CurrentColumn); } - RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding); + RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact, 0.0f); if (held) table->HeldHeaderColumn = (ImGuiTableColumnIdx)column_n; window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f; @@ -3277,7 +3421,9 @@ void ImGui::TableHeader(const char* label) } // Sort order arrow - const float ellipsis_max = ImMax(cell_r.Max.x - w_arrow - w_sort_text, label_pos.x); + // - Only clip label for a visible arrow. Auto-fit still accounts for a possible arrow, but when + // manually sized smaller we don't clip the label for the sake of an arrow that isn't displayed. + const float ellipsis_max = ImMax(cell_r.Max.x - (sort_arrow ? w_arrow + w_sort_text : 0.0f), label_pos.x); if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort)) { if (column->SortOrder != -1) @@ -3481,7 +3627,7 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label if (pass == 1) { // Draw border - draw_list->AddLine(bg_shape[0], bg_shape[3], TableGetColumnBorderCol(table, order_n, column_n)); + draw_list->AddLine(bg_shape[0] - ImVec2(TABLE_BORDER_SIZE * 0.5f, 0.0f), bg_shape[3] - ImVec2(TABLE_BORDER_SIZE * 0.5f, 0.0f), TableGetColumnBorderCol(table, order_n, column_n), TABLE_BORDER_SIZE); } } PopClipRect(); @@ -3597,17 +3743,20 @@ void ImGui::TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags want_separator = true; } - // Ordering - if (flags_for_section_to_display & ImGuiTableFlags_Reorderable) - { - if (MenuItem(LocalizeGetMsg(ImGuiLocKey_TableResetOrder), NULL, false, !table->IsDefaultDisplayOrder)) - table->IsResetDisplayOrderRequest = true; - want_separator = true; - } - - // Reset all (should work but seems unnecessary/noisy to expose?) - //if (MenuItem("Reset all")) - // table->IsResetAllRequest = true; + // Reset Order/Visibility etc. + if (flags_for_section_to_display & (ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable)) + if (BeginMenu(LocalizeGetMsg(ImGuiLocKey_TableReset))) + { + //if (MenuItem(LocalizeGetMsg(ImGuiLocKey_TableResetAll))) // FIXME: Hiding because altering Sort Order + requesting auto-fit simultaneously has a tendency to exhibit a sizing glitch. + // table->IsResetAllRequest = true; + if (flags_for_section_to_display & ImGuiTableFlags_Reorderable) + if (MenuItem(LocalizeGetMsg(ImGuiLocKey_TableResetOrder), NULL, false, !table->IsDefaultDisplayOrder)) // PS: cannot be hidden because it would mess with drag reordering. + table->IsResetDisplayOrderRequest = true; + if (flags_for_section_to_display & ImGuiTableFlags_Hideable) + if (MenuItem(LocalizeGetMsg(ImGuiLocKey_TableResetVisibility), NULL, false, !table->IsDefaultVisibility)) + table->IsResetVisibilityRequest = true; + EndMenu(); + } // Sorting // (modify TableOpenContextMenu() to add _Sortable flag if enabling this) @@ -3692,6 +3841,7 @@ void ImGui::TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags // - TableResetSettings() // - TableSaveSettings() [Internal] // - TableLoadSettings() [Internal] +// - TableLoadSettingsForColumns() [Internal] // - TableSettingsHandler_ClearAll() [Internal] // - TableSettingsHandler_ApplyAll() [Internal] // - TableSettingsHandler_ReadOpen() [Internal] @@ -3726,6 +3876,15 @@ static size_t TableSettingsCalcChunkSize(int columns_count) ImGuiTableSettings* ImGui::TableSettingsCreate(ImGuiID id, int columns_count) { ImGuiContext& g = *GImGui; + if (ImGuiTableSettings* settings = TableSettingsFindByID(id)) + { + if (settings->ColumnsCountMax >= columns_count) + { + TableSettingsInit(settings, id, columns_count, settings->ColumnsCountMax); // Recycle + return settings; + } + settings->ID = 0; // Invalidate storage, we won't fit because of a count change + } ImGuiTableSettings* settings = g.SettingsTables.alloc_chunk(TableSettingsCalcChunkSize(columns_count)); TableSettingsInit(settings, id, columns_count, columns_count); return settings; @@ -3745,16 +3904,12 @@ ImGuiTableSettings* ImGui::TableSettingsFindByID(ImGuiID id) // Get settings for a given table, NULL if none ImGuiTableSettings* ImGui::TableGetBoundSettings(ImGuiTable* table) { - if (table->SettingsOffset != -1) - { - ImGuiContext& g = *GImGui; - ImGuiTableSettings* settings = g.SettingsTables.ptr_from_offset(table->SettingsOffset); - IM_ASSERT(settings->ID == table->ID); - if (settings->ColumnsCountMax >= table->ColumnsCount) - return settings; // OK - settings->ID = 0; // Invalidate storage, we won't fit because of a count change - } - return NULL; + if (table->SettingsOffset == -1) + return NULL; + ImGuiContext& g = *GImGui; + ImGuiTableSettings* settings = g.SettingsTables.ptr_from_offset(table->SettingsOffset); + IM_ASSERT(settings->ID == table->ID); + return settings; } // Restore initial state of table (with or without saved settings) @@ -3775,12 +3930,18 @@ void ImGui::TableSaveSettings(ImGuiTable* table) // Bind or create settings data ImGuiContext& g = *GImGui; ImGuiTableSettings* settings = TableGetBoundSettings(table); + if (settings != NULL && table->ColumnsCount > settings->ColumnsCountMax) + { + settings->ID = 0; // Invalidate storage, we won't fit because of a count change + settings = NULL; + } if (settings == NULL) { settings = TableSettingsCreate(table->ID, table->ColumnsCount); table->SettingsOffset = g.SettingsTables.offset_from_ptr(settings); } settings->ColumnsCount = (ImGuiTableColumnIdx)table->ColumnsCount; + settings->LastUsedDate = g.SessionDate; // Serialize ImGuiTable/ImGuiTableColumn into ImGuiTableSettings/ImGuiTableColumnSettings IM_ASSERT(settings->ID == table->ID); @@ -3793,6 +3954,7 @@ void ImGui::TableSaveSettings(ImGuiTable* table) for (int n = 0; n < table->ColumnsCount; n++, column++, column_settings++) { const float width_or_weight = (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? column->StretchWeight : column->WidthRequest; + column_settings->ID = column->ID; column_settings->WidthOrWeight = width_or_weight; column_settings->Index = (ImGuiTableColumnIdx)n; column_settings->DisplayOrder = column->DisplayOrder; @@ -3824,9 +3986,11 @@ void ImGui::TableSaveSettings(ImGuiTable* table) void ImGui::TableLoadSettings(ImGuiTable* table) { ImGuiContext& g = *GImGui; - table->IsSettingsRequestLoad = false; if (table->Flags & ImGuiTableFlags_NoSavedSettings) + { + table->IsSettingsRequestLoad = false; // Done return; + } // Bind settings ImGuiTableSettings* settings; @@ -3846,44 +4010,84 @@ void ImGui::TableLoadSettings(ImGuiTable* table) table->SettingsLoadedFlags = settings->SaveFlags; table->RefScale = settings->RefScale; + settings->LastUsedDate = g.SessionDate; + // TableUpdateLayout() will then call TableLoadSettingsForColumns() to apply the data. +} - // Initialize default columns settings - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - { - ImGuiTableColumn* column = &table->Columns[column_n]; - TableInitColumnDefaults(table, column, ~0); - column->AutoFitQueue = 0x00; - } +// NB: This was written to be similar to the logic in TableReconcileColumns(). +void ImGui::TableLoadSettingsForColumns(ImGuiTable* table) +{ + for (ImGuiTableColumn& column : table->Columns) + column.IsLoadedSettings = false; + ImGuiTableSettings* settings = TableGetBoundSettings(table); + if (settings == NULL) + return; + + table->SettingsLoadedFlags |= ImGuiTableFlags_Reorderable; // We handle above in code above. // Serialize ImGuiTableSettings/ImGuiTableColumnSettings into ImGuiTable/ImGuiTableColumn ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings(); - for (int data_n = 0; data_n < settings->ColumnsCount; data_n++, column_settings++) + + // Fast path + int matches = 0; + for (int n = 0; n < table->ColumnsCount; n++) { - int column_n = column_settings->Index; - if (column_n < 0 || column_n >= table->ColumnsCount) - continue; - - ImGuiTableColumn* column = &table->Columns[column_n]; - if (settings->SaveFlags & ImGuiTableFlags_Resizable) - { - if (column_settings->IsStretch) - column->StretchWeight = column_settings->WidthOrWeight; - else - column->WidthRequest = column_settings->WidthOrWeight; - } - if (settings->SaveFlags & ImGuiTableFlags_Reorderable) - column->DisplayOrder = column_settings->DisplayOrder; - if ((settings->SaveFlags & ImGuiTableFlags_Hideable) && column_settings->IsEnabled != -1) - column->IsUserEnabled = column->IsUserEnabledNextFrame = column_settings->IsEnabled == 1; - column->SortOrder = column_settings->SortOrder; - column->SortDirection = column_settings->SortDirection; + if (n >= settings->ColumnsCount || column_settings[n].ID != table->Columns[n].ID) + break; + TableLoadSettingsForColumn(&table->Columns[n], &column_settings[n], settings->SaveFlags); + matches++; } + if (matches == settings->ColumnsCount) + return; + const int settings_start_n = matches; // Small optimization + for (int n = settings_start_n; n < settings->ColumnsCount; n++) + column_settings[n].IsLoaded = false; - // Fix display order and build index - if (settings->SaveFlags & ImGuiTableFlags_Reorderable) - TableFixDisplayOrder(table); - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - table->DisplayOrderToIndex[table->Columns[column_n].DisplayOrder] = (ImGuiTableColumnIdx)column_n; + // Find matches for named columns + for (ImGuiTableColumn& column : table->Columns) + if (column.ID != 0 && !column.IsLoadedSettings) + for (int n = settings_start_n; n < settings->ColumnsCount; n++) + if (column_settings[n].ID == column.ID && !column_settings[n].IsLoaded) + { + TableLoadSettingsForColumn(&column, &column_settings[n], settings->SaveFlags); + matches++; + break; + } + + // Remaining entries are matched sequentially + int dst_idx = 0; + for (int n = settings_start_n; n < settings->ColumnsCount; n++) + if (!column_settings[n].IsLoaded) + { + while (dst_idx < table->ColumnsCount && table->Columns[dst_idx].IsLoadedSettings) + dst_idx++; + if (dst_idx >= table->ColumnsCount) + break; + TableLoadSettingsForColumn(&table->Columns[dst_idx], &column_settings[n], settings->SaveFlags); + dst_idx++; + } +} + +void ImGui::TableLoadSettingsForColumn(ImGuiTableColumn* column, ImGuiTableColumnSettings* column_settings, ImGuiTableFlags load_flags) +{ + column->IsLoadedSettings = true; + column_settings->IsLoaded = true; + if (load_flags & ImGuiTableFlags_Resizable) + { + if (column_settings->IsStretch) + column->StretchWeight = column_settings->WidthOrWeight; + else + column->WidthRequest = column_settings->WidthOrWeight; + column->AutoFitQueue = 0x00; + } + if (load_flags & ImGuiTableFlags_Reorderable) + column->DisplayOrder = column_settings->DisplayOrder; + else + column->DisplayOrder = column_settings->Index; // Because default depends on previous Index, we need to set that up and cannot rely on TableInitColumnDefaults() + if ((load_flags & ImGuiTableFlags_Hideable) && column_settings->IsEnabled != -1) + column->IsUserEnabled = column->IsUserEnabledNextFrame = (column_settings->IsEnabled == 1); + column->SortOrder = column_settings->SortOrder; + column->SortDirection = column_settings->SortDirection; } struct ImGuiTableFixDisplayOrderColumnData @@ -3898,7 +4102,8 @@ static int IMGUI_CDECL TableFixDisplayOrderComparer(const void* lhs, const void* const ImGuiTable* table = ((const ImGuiTableFixDisplayOrderColumnData*)lhs)->Table; const ImGuiTableColumnIdx lhs_idx = ((const ImGuiTableFixDisplayOrderColumnData*)lhs)->Idx; const ImGuiTableColumnIdx rhs_idx = ((const ImGuiTableFixDisplayOrderColumnData*)rhs)->Idx; - const int order_delta = (table->Columns[lhs_idx].DisplayOrder - table->Columns[rhs_idx].DisplayOrder); + // Assume ImGuiTableColumnIdx == signed short, to turn -1 into a large value so that it always sort after. + const int order_delta = ((unsigned short)table->Columns[lhs_idx].DisplayOrder - (unsigned short)table->Columns[rhs_idx].DisplayOrder); return (order_delta > 0) ? +1 : (order_delta < 0) ? -1 : (lhs_idx > rhs_idx) ? +1 : -1; } @@ -3916,6 +4121,8 @@ void ImGui::TableFixDisplayOrder(ImGuiTable* table) ImQsort(fdo_columns, (size_t)table->ColumnsCount, sizeof(ImGuiTableFixDisplayOrderColumnData), TableFixDisplayOrderComparer); for (int n = 0; n < table->ColumnsCount; n++) table->Columns[fdo_columns[n].Idx].DisplayOrder = (ImGuiTableColumnIdx)n; + for (int n = 0; n < table->ColumnsCount; n++) + table->DisplayOrderToIndex[table->Columns[n].DisplayOrder] = (ImGuiTableColumnIdx)n; } static void TableSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*) @@ -3927,6 +4134,24 @@ static void TableSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandle g.SettingsTables.clear(); } +static void TableSettingsHandler_Cleanup(ImGuiContext* ctx, ImGuiSettingsHandler*, ImGuiSettingsCleanupArgs* args) +{ + ImGuiContext& g = *ctx; + for (int i = 0; i != g.Tables.GetMapSize(); i++) + if (ImGuiTable* table = g.Tables.TryGetMapData(i)) + table->SettingsOffset = -1; + for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) + { + const bool is_valid = settings->LastUsedDate.IsValid(); + if (args->_DiscardOlderThanDate != 0 && settings->LastUsedDate.Unpack() < args->_DiscardOlderThanDate) + settings->ID = 0; + else if (args->DiscardWhenMissingDate && !is_valid) + settings->ID = 0; + else if (args->SetCurrentSessionDateToAll || (args->SetCurrentSessionDateWhenMissingDate && !is_valid)) + settings->LastUsedDate = g.SessionDate; + } +} + // Apply to existing windows (if any) static void TableSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*) { @@ -3941,20 +4166,13 @@ static void TableSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandle static void* TableSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) { + // FIXME: As topology changes are allowed, strictly speaking a >= IMGUI_TABLE_MAX_COLUMNS tables stored in .ini file + // that was emitted with a higher max count could still be meaningful in some unlikely cases. + // We might want to use another MAX defined as (1<<(sizeof(ImGuiTableColumnIdx)*8-1))-1. ImGuiID id = 0; int columns_count = 0; - if (sscanf(name, "0x%08X,%d", &id, &columns_count) < 2) + if (sscanf(name, "0x%08X,%d", &id, &columns_count) < 2 || columns_count <= 0 || columns_count >= IMGUI_TABLE_MAX_COLUMNS) return NULL; - - if (ImGuiTableSettings* settings = ImGui::TableSettingsFindByID(id)) - { - if (settings->ColumnsCountMax >= columns_count) - { - TableSettingsInit(settings, id, columns_count, settings->ColumnsCountMax); // Recycle - return settings; - } - settings->ID = 0; // Invalidate storage, we won't fit because of a count change - } return ImGui::TableSettingsCreate(id, columns_count); } @@ -3966,6 +4184,7 @@ static void TableSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, int column_n = 0, r = 0, n = 0; if (sscanf(line, "RefScale=%f", &f) == 1) { settings->RefScale = f; return; } + if (sscanf(line, "LastUsed=%d", &n) == 1) { settings->LastUsedDate = n; return; } if (sscanf(line, "Column %d%n", &column_n, &r) == 1) { @@ -3975,12 +4194,13 @@ static void TableSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, char c = 0; ImGuiTableColumnSettings* column = settings->GetColumnSettings() + column_n; column->Index = (ImGuiTableColumnIdx)column_n; - if (sscanf(line, "UserID=0x%08X%n", (ImU32*)&n, &r)==1) { line = ImStrSkipBlank(line + r); column->UserID = (ImGuiID)n; } + if (sscanf(line, "UserID=0x%08X%n", (ImU32*)&n, &r)==1) { line = ImStrSkipBlank(line + r); } // FIXME-LEGACY: Removed 2025/11/12, was never properly set. if (sscanf(line, "Width=%d%n", &n, &r) == 1) { line = ImStrSkipBlank(line + r); column->WidthOrWeight = (float)n; column->IsStretch = 0; settings->SaveFlags |= ImGuiTableFlags_Resizable; } if (sscanf(line, "Weight=%f%n", &f, &r) == 1) { line = ImStrSkipBlank(line + r); column->WidthOrWeight = f; column->IsStretch = 1; settings->SaveFlags |= ImGuiTableFlags_Resizable; } if (sscanf(line, "Visible=%d%n", &n, &r) == 1) { line = ImStrSkipBlank(line + r); column->IsEnabled = (ImU8)n; settings->SaveFlags |= ImGuiTableFlags_Hideable; } if (sscanf(line, "Order=%d%n", &n, &r) == 1) { line = ImStrSkipBlank(line + r); column->DisplayOrder = (ImGuiTableColumnIdx)n; settings->SaveFlags |= ImGuiTableFlags_Reorderable; } if (sscanf(line, "Sort=%d%c%n", &n, &c, &r) == 2) { line = ImStrSkipBlank(line + r); column->SortOrder = (ImGuiTableColumnIdx)n; column->SortDirection = (c == '^') ? ImGuiSortDirection_Descending : ImGuiSortDirection_Ascending; settings->SaveFlags |= ImGuiTableFlags_Sortable; } + if (sscanf(line, "ID=0x%08X%n", (ImU32*)&n, &r) == 1) { line = ImStrSkipBlank(line + r); column->ID = (ImGuiID)n; } } } @@ -4008,18 +4228,21 @@ 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); + bool save_column = 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); } if (save_size && !column->IsStretch) { buf->appendf(" Width=%d", (int)column->WidthOrWeight); } if (save_visible) { buf->appendf(" Visible=%d", column->IsEnabled); } if (save_order) { buf->appendf(" Order=%d", column->DisplayOrder); } if (save_sort && column->SortOrder != -1) { buf->appendf(" Sort=%d%c", column->SortOrder, (column->SortDirection == ImGuiSortDirection_Ascending) ? 'v' : '^'); } + if (column->ID != 0) { buf->appendf(" ID=0x%08X", column->ID); } buf->append("\n"); } + if (g.IO.ConfigIniSettingsSaveLastUsedDate) + if (int last_used_date = settings->LastUsedDate.Unpack()) + buf->appendf("LastUsed=%08d\n", last_used_date); buf->append("\n"); } } @@ -4030,6 +4253,7 @@ void ImGui::TableSettingsAddSettingsHandler() ini_handler.TypeName = "Table"; ini_handler.TypeHash = ImHashStr("Table"); ini_handler.ClearAllFn = TableSettingsHandler_ClearAll; + ini_handler.CleanupFn = TableSettingsHandler_Cleanup; ini_handler.ReadOpenFn = TableSettingsHandler_ReadOpen; ini_handler.ReadLineFn = TableSettingsHandler_ReadLine; ini_handler.ApplyAllFn = TableSettingsHandler_ApplyAll; @@ -4075,11 +4299,12 @@ void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table) void ImGui::TableGcCompactTransientBuffers(ImGuiTableTempData* temp_data) { + temp_data->AngledHeadersRequests.clear(); temp_data->DrawSplitter.ClearFreeMemory(); temp_data->LastTimeActive = -1.0f; } -// Compact and remove unused settings data (currently only used by TestEngine) +// Compact and remove unused or resize settings data (only TestEngine mark unused, but resizing table down can lead to compaction) void ImGui::TableGcCompactSettings() { ImGuiContext& g = *GImGui; @@ -4095,6 +4320,9 @@ void ImGui::TableGcCompactSettings() if (settings->ID != 0) memcpy(new_chunk_stream.alloc_chunk(TableSettingsCalcChunkSize(settings->ColumnsCount)), settings, TableSettingsCalcChunkSize(settings->ColumnsCount)); g.SettingsTables.swap(new_chunk_stream); + for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++) + if (ImGuiTable* table = g.Tables.TryGetMapData(table_n)) + table->SettingsOffset = -1; } @@ -4102,6 +4330,7 @@ void ImGui::TableGcCompactSettings() // [SECTION] Tables: Debugging //------------------------------------------------------------------------- // - DebugNodeTable() [Internal] +// - DebugNodeTableSettings() [Internal] //------------------------------------------------------------------------- #ifndef IMGUI_DISABLE_DEBUG_TOOLS @@ -4165,13 +4394,13 @@ void ImGui::DebugNodeTable(ImGuiTable* table) "WidthGiven: %.1f, Request/Auto: %.1f/%.1f, StretchWeight: %.3f (%.1f%%)\n" "MinX: %.1f, MaxX: %.1f (%+.1f), ClipRect: %.1f to %.1f (+%.1f)\n" "ContentWidth: %.1f,%.1f, HeadersUsed/Ideal %.1f/%.1f\n" - "Sort: %d%s, UserID: 0x%08X, Flags: 0x%04X: %s%s%s..", + "Sort: %d%s, UserData: 0x%08X, Flags: 0x%04X: %s%s%s..", n, column->DisplayOrder, name, column->MinX - table->WorkRect.Min.x, column->MaxX - table->WorkRect.Min.x, (n < table->FreezeColumnsRequest) ? " (Frozen)" : "", column->IsEnabled, column->IsVisibleX, column->IsVisibleY, column->IsRequestOutput, column->IsSkipItems, column->DrawChannelFrozen, column->DrawChannelUnfrozen, column->WidthGiven, column->WidthRequest, column->WidthAuto, column->StretchWeight, column->StretchWeight > 0.0f ? (column->StretchWeight / sum_weights) * 100.0f : 0.0f, column->MinX, column->MaxX, column->MaxX - column->MinX, column->ClipRect.Min.x, column->ClipRect.Max.x, column->ClipRect.Max.x - column->ClipRect.Min.x, column->ContentMaxXFrozen - column->WorkMinX, column->ContentMaxXUnfrozen - column->WorkMinX, column->ContentMaxXHeadersUsed - column->WorkMinX, column->ContentMaxXHeadersIdeal - column->WorkMinX, - column->SortOrder, (column->SortDirection == ImGuiSortDirection_Ascending) ? " (Asc)" : (column->SortDirection == ImGuiSortDirection_Descending) ? " (Des)" : "", column->UserID, column->Flags, + column->SortOrder, (column->SortDirection == ImGuiSortDirection_Ascending) ? " (Asc)" : (column->SortDirection == ImGuiSortDirection_Descending) ? " (Des)" : "", column->UserData, column->Flags, (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? "WidthStretch " : "", (column->Flags & ImGuiTableColumnFlags_WidthFixed) ? "WidthFixed " : "", (column->Flags & ImGuiTableColumnFlags_NoResize) ? "NoResize " : ""); @@ -4184,34 +4413,45 @@ void ImGui::DebugNodeTable(ImGuiTable* table) } } if (ImGuiTableSettings* settings = TableGetBoundSettings(table)) - DebugNodeTableSettings(settings); + DebugNodeTableSettings(settings, table); if (clear_settings) - table->IsResetAllRequest = true; + table->IsResetAllRequest = true; // Queue a call to TableResetSettings() TreePop(); } -void ImGui::DebugNodeTableSettings(ImGuiTableSettings* settings) +void ImGui::DebugNodeTableSettings(ImGuiTableSettings* settings, ImGuiTable* table) { - if (!TreeNode((void*)(intptr_t)settings->ID, "Settings 0x%08X (%d columns)", settings->ID, settings->ColumnsCount)) - return; - BulletText("SaveFlags: 0x%08X", settings->SaveFlags); - BulletText("ColumnsCount: %d (max %d)", settings->ColumnsCount, settings->ColumnsCountMax); - for (int n = 0; n < settings->ColumnsCount; n++) + if (settings->ID == 0) + PushID(settings); + const bool open = TreeNode((void*)(intptr_t)settings->ID, "Settings 0x%08X (%d columns)", settings->ID, settings->ColumnsCount); + const bool hovered = IsItemHovered(); + if (hovered && table == NULL && settings->ID != 0) + table = TableFindByID(settings->ID); + if (hovered && table != NULL) + GetForegroundDrawList(table->OuterWindow)->AddRect(table->OuterRect.Min, table->OuterRect.Max, IM_COL32(255, 255, 0, 255)); + if (open) { - ImGuiTableColumnSettings* column_settings = &settings->GetColumnSettings()[n]; - ImGuiSortDirection sort_dir = (column_settings->SortOrder != -1) ? (ImGuiSortDirection)column_settings->SortDirection : ImGuiSortDirection_None; - BulletText("Column %d Order %d SortOrder %d %s Vis %d %s %7.3f UserID 0x%08X", - n, column_settings->DisplayOrder, column_settings->SortOrder, - (sort_dir == ImGuiSortDirection_Ascending) ? "Asc" : (sort_dir == ImGuiSortDirection_Descending) ? "Des" : "---", - column_settings->IsEnabled, column_settings->IsStretch ? "Weight" : "Width ", column_settings->WidthOrWeight, column_settings->UserID); + BulletText("SaveFlags: 0x%08X", settings->SaveFlags); + BulletText("ColumnsCount: %d (max %d)", settings->ColumnsCount, settings->ColumnsCountMax); + for (int n = 0; n < settings->ColumnsCount; n++) + { + ImGuiTableColumnSettings* column_settings = &settings->GetColumnSettings()[n]; + ImGuiSortDirection sort_dir = (column_settings->SortOrder != -1) ? (ImGuiSortDirection)column_settings->SortDirection : ImGuiSortDirection_None; + BulletText("Column %d Order %d SortOrder %2d %s Vis %d %s %7.3f ID 0x%08X", + n, column_settings->DisplayOrder, column_settings->SortOrder, + (sort_dir == ImGuiSortDirection_Ascending) ? "Asc" : (sort_dir == ImGuiSortDirection_Descending) ? "Des" : "---", + column_settings->IsEnabled, column_settings->IsStretch ? "Weight" : "Width ", column_settings->WidthOrWeight, column_settings->ID); + } + TreePop(); } - TreePop(); + if (settings->ID == 0) + PopID(); } #else // #ifndef IMGUI_DISABLE_DEBUG_TOOLS void ImGui::DebugNodeTable(ImGuiTable*) {} -void ImGui::DebugNodeTableSettings(ImGuiTableSettings*) {} +void ImGui::DebugNodeTableSettings(ImGuiTableSettings*, ImGuiTable*) {} #endif diff --git a/3rdparty/imgui/src/imgui_widgets.cpp b/3rdparty/imgui/src/imgui_widgets.cpp index ea792f9e8e..5ed7d6febf 100644 --- a/3rdparty/imgui/src/imgui_widgets.cpp +++ b/3rdparty/imgui/src/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 +// dear imgui, v1.92.9b // (widgets code) /* @@ -266,6 +266,8 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags) } } +// Note that all functions taking format strings in the API may be passed ("%s", text) or ("%.*s", text_len, text), +// which will automatically bypass the formatter. void ImGui::TextUnformatted(const char* text, const char* text_end) { TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); @@ -1272,7 +1274,7 @@ bool ImGui::Checkbox(const char* label, bool* v) // Range-Selection/Multi-selection support (footer) if (is_multi_select) - MultiSelectItemFooter(id, &checked, &pressed); + MultiSelectItemFooter(id, &checked, &pressed, ImGuiMultiSelectFlags_CheckboxMode_); else if (pressed) checked = !checked; @@ -1323,7 +1325,7 @@ bool ImGui::CheckboxFlagsT(const char* label, T* flags, T flags_value) if (!all_on && any_on) { ImGuiContext& g = *GImGui; - g.NextItemData.ItemFlags |= ImGuiItemFlags_MixedValue; + g.NextItemData.ItemFlagsSet |= ImGuiItemFlags_MixedValue; pressed = Checkbox(label, &all_on); } else @@ -1575,7 +1577,7 @@ bool ImGui::TextLinkOpenURL(const char* label, const char* url) bool pressed = TextLink(label); if (pressed && g.PlatformIO.Platform_OpenInShellFn != NULL) g.PlatformIO.Platform_OpenInShellFn(&g, url); - SetItemTooltip(LocalizeGetMsg(ImGuiLocKey_OpenLink_s), url); // It is more reassuring for user to _always_ display URL when we same as label + SetItemTooltip(LocalizeGetMsg(ImGuiLocKey_OpenLink_s), url); // It is more reassuring for user to _always_ display URL when it is the same as the label. if (BeginPopupContextItem()) { if (MenuItem(LocalizeGetMsg(ImGuiLocKey_CopyLink))) @@ -1746,14 +1748,14 @@ void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end const float separator_thickness = style.SeparatorTextBorderSize; const ImVec2 min_size(label_size.x + extra_w + padding.x * 2.0f, ImMax(label_size.y + padding.y * 2.0f, separator_thickness)); const ImRect bb(pos, ImVec2(window->WorkRect.Max.x, pos.y + min_size.y)); - const float text_baseline_y = ImTrunc((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.99999f); //ImMax(padding.y, ImTrunc((style.SeparatorTextSize - label_size.y) * 0.5f)); + const float text_baseline_y = ImTrunc((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.999f); //ImMax(padding.y, ImTrunc((style.SeparatorTextSize - label_size.y) * 0.5f)); ItemSize(min_size, text_baseline_y); if (!ItemAdd(bb, id)) return; const float sep1_x1 = pos.x; const float sep2_x2 = bb.Max.x; - const float seps_y = ImTrunc((bb.Min.y + bb.Max.y) * 0.5f + 0.99999f); + const float seps_y = ImTrunc((bb.Min.y + bb.Max.y) * 0.5f + 0.999f); const float label_avail_w = ImMax(0.0f, sep2_x2 - sep1_x1 - padding.x * 2.0f); const ImVec2 label_pos(pos.x + padding.x + ImMax(0.0f, (label_avail_w - label_size.x - extra_w) * style.SeparatorTextAlign.x), pos.y + text_baseline_y); // FIXME-ALIGN @@ -3720,8 +3722,9 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - const bool init = (g.TempInputId != id); - if (init) + const bool is_deactivated = (g.InputTextDeactivatedState.ID == id); + const bool is_active = (g.TempInputId == id); + if (!is_active && !is_deactivated) ClearActiveID(); ImVec2 backup_pos = window->DC.CursorPos; @@ -3729,13 +3732,13 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* g.LastItemData.ItemFlags |= ImGuiItemFlags_AllowDuplicateId; // Using ImGuiInputTextFlags_MergedItem above will skip ItemAdd() so we poke here. bool value_changed = InputTextEx(label, NULL, buf, (int)buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_TempInput | ImGuiInputTextFlags_AutoSelectAll, callback, user_data); KeepAliveID(id); // Not done because of ImGuiInputTextFlags_TempInput - if (init) + if (!is_active && !is_deactivated) { // First frame we started displaying the InputText widget, we expect it to take the active id. IM_ASSERT(g.ActiveId == id); g.TempInputId = g.ActiveId; } - if (g.ActiveId != id) + if (is_active && g.ActiveId != id) g.TempInputId = 0; window->DC.CursorPos = backup_pos; return value_changed; @@ -3760,6 +3763,7 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint; g.LastItemData.ItemFlags |= ImGuiItemFlags_NoMarkEdited; // Because TempInputText() uses ImGuiInputTextFlags_MergedItem it doesn't submit a new item, so we poke LastItemData. + g.LastItemData.ItemFlags = (g.LastItemData.ItemFlags & ImGuiItemFlags_LiveEditOnInputScalar) ? (g.LastItemData.ItemFlags | ImGuiItemFlags_LiveEditOnInputText) : (g.LastItemData.ItemFlags & ~ImGuiItemFlags_LiveEditOnInputText); if (!TempInputText(bb, id, label, data_buf, IM_COUNTOF(data_buf), flags)) return false; @@ -3817,7 +3821,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data // Disable the MarkItemEdited() call in InputText but keep ImGuiItemStatusFlags_Edited. // We call MarkItemEdited() ourselves by comparing the actual data rather than the string. - g.NextItemData.ItemFlags |= ImGuiItemFlags_NoMarkEdited; + g.NextItemData.ItemFlagsSet |= ImGuiItemFlags_NoMarkEdited; flags |= ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint; const bool has_step_buttons = (p_step != NULL); @@ -3839,8 +3843,23 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data } // Apply - bool input_edited = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_EditedInternal) != 0; // We would be using 'ret' if ImGuiInputTextFlags_EnterReturnsTrue was not involved. - bool value_changed = input_edited ? DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL) : false; + bool value_changed = false; + if (g.LastItemData.ItemFlags & ImGuiItemFlags_LiveEditOnInputScalar) + { + bool input_edited = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_EditedInternal) != 0; // We would be using 'ret' if ImGuiInputTextFlags_EnterReturnsTrue was not involved. + if (input_edited) + value_changed = DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL); + } + else + { + //g.LastItemData.StatusFlags &= ~ImGuiItemStatusFlags_Edited; + if (g.DeactivatedItemData.ID == g.LastItemData.ID) + { + //g.DeactivatedItemData.HasBeenEditedBefore = false; // Will be set below by MarkItemEdited() + //if (IsItemDeactivated()) // Should be unnecessary + value_changed = DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL); + } + } // Step buttons if (has_step_buttons) @@ -4582,10 +4601,14 @@ void ImGui::InputTextDeactivateHook(ImGuiID id) { ImGuiContext& g = *GImGui; ImGuiInputTextState* state = &g.InputTextState; - if (id == 0 || state->ID != id) + if (id == 0 || state->ID != id || g.ActiveId != id) + return; + if (!state->EditedBefore) return; //IMGUI_DEBUG_LOG_ACTIVEID("InputTextDeactivateHook() id = 0x%08X\n", id); + state->EditedBefore = false; g.InputTextDeactivatedState.ID = state->ID; + g.InputTextDeactivatedState.ElapseFrame = g.FrameCount + 1; if (state->Flags & ImGuiInputTextFlags_ReadOnly) { g.InputTextDeactivatedState.TextA.resize(0); // In theory this data won't be used, but clear to be neat. @@ -4815,7 +4838,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const bool user_clicked = hovered && io.MouseClicked[0]; const bool input_requested_by_nav = (g.ActiveId != id) && (g.NavActivateId == id); const bool input_requested_by_reactivate = (g.InputTextReactivateId == id); // for io.ConfigInputTextEnterKeepActive - const bool input_requested_by_user = (user_clicked) || (g.ActiveId == 0 && (flags & ImGuiInputTextFlags_TempInput)); + const bool input_requested_by_user = (user_clicked) || (g.ActiveId == 0 && (flags & ImGuiInputTextFlags_TempInput) && g.InputTextDeactivatedState.ID != id); const ImGuiID scrollbar_id = (is_multiline && state != NULL) ? GetWindowScrollbarID(draw_window, ImGuiAxis_Y) : 0; const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == scrollbar_id; const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == scrollbar_id; @@ -4847,7 +4870,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ state->CursorAnimReset(); // Backup state of deactivating item so they'll have a chance to do a write to output buffer on the same frame they report IsItemDeactivatedAfterEdit (#4714) - InputTextDeactivateHook(state->ID); + if (state->ID != id && state->ID == g.ActiveId && (init_make_active && g.ActiveId != id)) //-V560 + InputTextDeactivateHook(state->ID); // <-- this is essentially an earlier call to what SetActiveID() would do below. // Take a copy of the initial buffer value. // From the moment we focused we are normally ignoring the content of 'buf' (unless we are in read-only mode) @@ -4974,6 +4998,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (state != NULL && state->ID == id) { state->Flags = flags; + //state->LastFrameActive = g.FrameCount; // Word-wrapping: attempt to keep cursor in view while resizing frame/parent (FIXME-WORDWRAP: would be better to preserve same relative offset) if (is_wordwrap && state->WrapWidth != wrap_width) @@ -5171,7 +5196,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (!is_new_line) { validated = clear_active_id = true; - if (io.ConfigInputTextEnterKeepActive && !is_multiline) + if (io.ConfigInputTextEnterKeepActive && !is_multiline && !is_ctrl_enter && !is_shift_enter) { // Queue reactivation, so that e.g. IsItemDeactivatedAfterEdit() will work. (#9001) state->SelectAll(); // No need to scroll @@ -5378,18 +5403,33 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ IM_ASSERT(callback_data.BufTextLen == (int)ImStrlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! InputTextReconcileUndoState(state, state->CallbackTextBackup.Data, state->CallbackTextBackup.Size - 1, callback_data.Buf, callback_data.BufTextLen); state->TextLen = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen() + state->EditedBefore = state->EditedThisFrame = true; state->CursorAnimReset(); } } } - // Will copy result string if modified. + // Write back result string if modified. // FIXME-OPT: Could mark dirty state from the stb_textedit callbacks - if (!is_readonly && strcmp(state->TextSrc, buf) != 0) + if (!is_readonly) { - apply_new_text = state->TextSrc; - apply_new_text_length = state->TextLen; - value_changed = true; + if (g.LastItemData.ItemFlags & ImGuiItemFlags_LiveEditOnInputText) + { + // Apply when modified + if (strcmp(state->TextSrc, buf) != 0) + { + apply_new_text = state->TextSrc; + apply_new_text_length = state->TextLen; + value_changed = true; + } + } + else + { + // Apply on validation/deactivation, otherwise cancel out previous apply attempts (e.g. revert) + value_changed = ((validated || clear_active_id || revert_edit) && strcmp(state->TextSrc, buf) != 0); + apply_new_text = value_changed ? state->TextSrc : NULL; + apply_new_text_length = value_changed ? state->TextLen : 0; + } } } @@ -5397,13 +5437,15 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // This is used when e.g. losing focus or tabbing out into another InputText() which may already be using the temp buffer. if (g.InputTextDeactivatedState.ID == id) { - if (g.ActiveId != id && IsItemDeactivatedAfterEdit() && !is_readonly && strcmp(g.InputTextDeactivatedState.TextA.Data, buf) != 0) - { - apply_new_text = g.InputTextDeactivatedState.TextA.Data; - apply_new_text_length = g.InputTextDeactivatedState.TextA.Size - 1; - value_changed = true; - //IMGUI_DEBUG_LOG("InputText(): apply Deactivated data for 0x%08X: \"%.*s\".\n", id, apply_new_text_length, apply_new_text); - } + // The state only exists after an Edit. IsItemDeactivatedAfterEdit() is not valid in every code path (see "widgets_inputtext_status_noliveedit" test). + if ((g.ActiveId != id && IsItemDeactivated()) || (g.ActiveId == id && (flags & ImGuiInputTextFlags_TempInput))) + if (!is_readonly && strcmp(g.InputTextDeactivatedState.TextA.Data, buf) != 0) + { + apply_new_text = g.InputTextDeactivatedState.TextA.Data; + apply_new_text_length = g.InputTextDeactivatedState.TextA.Size - 1; + value_changed = true; + //IMGUI_DEBUG_LOG("InputText(): apply Deactivated data for 0x%08X: \"%.*s\".\n", id, apply_new_text_length, apply_new_text); + } g.InputTextDeactivatedState.ID = 0; } @@ -5443,7 +5485,10 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value) // Otherwise request text input ahead for next frame. if (g.ActiveId == id && clear_active_id) + { + state->EditedBefore = false; // Data already applied: avoid InputTextDeactivateHook() taking a record now or later if same id is activated again without editing. ClearActiveID(); + } // Render frame if (!is_multiline) @@ -5641,7 +5686,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ ImVec2 cursor_screen_pos = ImTrunc(draw_pos + cursor_offset - draw_scroll); ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f); if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) - draw_window->DrawList->AddLineV(cursor_screen_rect.Min.x, cursor_screen_rect.Min.y, cursor_screen_rect.Max.y, GetColorU32(ImGuiCol_InputTextCursor), 1.0f * (float)(int)style._MainScale); // FIXME-DPI: Cursor thickness (#7031) + draw_window->DrawList->AddLineV(cursor_screen_rect.Min.x, cursor_screen_rect.Min.y, cursor_screen_rect.Max.y, GetColorU32(ImGuiCol_InputTextCursor), style.InputTextCursorSize); // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) // This is required for some backends (SDL3) to start emitting character/text inputs. @@ -5664,7 +5709,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { // For focus requests to work on our multiline we need to ensure our child ItemAdd() call specifies the ImGuiItemFlags_Inputable (see #4761, #7870)... Dummy(ImVec2(0.0f, text_size_y + style.FramePadding.y)); - g.NextItemData.ItemFlags |= (ImGuiItemFlags)ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop; + g.NextItemData.ItemFlagsSet |= (ImGuiItemFlags)ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop; EndChild(); item_data_backup.StatusFlags |= (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredWindow); @@ -5749,7 +5794,6 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state) // - RenderColorRectWithAlphaCheckerboard() [Internal] // - ColorPicker4() // - ColorButton() -// - SetColorEditOptions() // - ColorTooltip() [Internal] // - ColorEditOptionsPopup() [Internal] // - ColorPickerOptionsPopup() [Internal] @@ -5821,14 +5865,14 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag // Read stored options if (!(flags & ImGuiColorEditFlags_DisplayMask_)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DisplayMask_); + flags |= (g.IO.ConfigColorEditFlags & ImGuiColorEditFlags_DisplayMask_); if (!(flags & ImGuiColorEditFlags_DataTypeMask_)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DataTypeMask_); + flags |= (g.IO.ConfigColorEditFlags & ImGuiColorEditFlags_DataTypeMask_); if (!(flags & ImGuiColorEditFlags_PickerMask_)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_); + flags |= (g.IO.ConfigColorEditFlags & ImGuiColorEditFlags_PickerMask_); if (!(flags & ImGuiColorEditFlags_InputMask_)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags_InputMask_); - flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_)); + flags |= (g.IO.ConfigColorEditFlags & ImGuiColorEditFlags_InputMask_); + flags |= (g.IO.ConfigColorEditFlags & ~(ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_)); IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DisplayMask_)); // Check that only 1 is selected IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_)); // Check that only 1 is selected @@ -6079,7 +6123,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl ImGuiIO& io = g.IO; const float width = CalcItemWidth(); - const bool is_readonly = ((g.NextItemData.ItemFlags | g.CurrentItemFlags) & ImGuiItemFlags_ReadOnly) != 0; + const bool is_readonly = ((g.NextItemData.ItemFlagsSet | g.CurrentItemFlags) & ImGuiItemFlags_ReadOnly) != 0; g.NextItemData.ClearFlags(); PushID(label); @@ -6097,13 +6141,13 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl // Read stored options if (!(flags & ImGuiColorEditFlags_PickerMask_)) - flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_PickerMask_; + flags |= ((g.IO.ConfigColorEditFlags & ImGuiColorEditFlags_PickerMask_) ? g.IO.ConfigColorEditFlags : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_PickerMask_; if (!(flags & ImGuiColorEditFlags_InputMask_)) - flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_InputMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_InputMask_; + flags |= ((g.IO.ConfigColorEditFlags & ImGuiColorEditFlags_InputMask_) ? g.IO.ConfigColorEditFlags : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_InputMask_; IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_PickerMask_)); // Check that only 1 is selected IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_)); // Check that only 1 is selected if (!(flags & ImGuiColorEditFlags_NoOptions)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar); + flags |= (g.IO.ConfigColorEditFlags & ImGuiColorEditFlags_AlphaBar); // Setup int components = (flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4; @@ -6125,10 +6169,11 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width)*0.5f, picker_pos.y + sv_picker_size * 0.5f); // Note: the triangle is displayed rotated with triangle_pa pointing to Hue, but most coordinates stays unrotated for logic. + const bool triangle_rotate = (flags & ImGuiColorEditFlags_PickerNoRotate) == 0; float triangle_r = wheel_r_inner - (int)(sv_picker_size * 0.027f); - ImVec2 triangle_pa = ImVec2(triangle_r, 0.0f); // Hue point. - ImVec2 triangle_pb = ImVec2(triangle_r * -0.5f, triangle_r * -0.866025f); // Black point. - ImVec2 triangle_pc = ImVec2(triangle_r * -0.5f, triangle_r * +0.866025f); // White point. + ImVec2 triangle_pa = triangle_rotate ? ImVec2(triangle_r, 0.0f) : ImVec2(triangle_r * +0.866f, triangle_r * +0.5f); // Hue point. + ImVec2 triangle_pb = triangle_rotate ? ImVec2(triangle_r * -0.5f, triangle_r * -0.866f) : ImVec2(0.0f, -triangle_r); // Black point. + ImVec2 triangle_pc = triangle_rotate ? ImVec2(triangle_r * -0.5f, triangle_r * +0.866f) : ImVec2(triangle_r * -0.866f, triangle_r * +0.5f); // White point. float H = col[0], S = col[1], V = col[2]; float R = col[0], G = col[1], B = col[2]; @@ -6163,8 +6208,8 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl H += 1.0f; value_changed = value_changed_h = true; } - float cos_hue_angle = ImCos(-H * 2.0f * IM_PI); - float sin_hue_angle = ImSin(-H * 2.0f * IM_PI); + float cos_hue_angle = triangle_rotate ? ImCos(-H * 2.0f * IM_PI) : 1.0f; + float sin_hue_angle = triangle_rotate ? ImSin(-H * 2.0f * IM_PI) : 0.0f; if (ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle))) { // Interacting with SV triangle @@ -6373,6 +6418,11 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments); draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad + 1, col_midgrey, hue_cursor_segments); draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, col_white, hue_cursor_segments); + if (triangle_rotate == false) + { + cos_hue_angle = 1.0f; + sin_hue_angle = 0.0f; + } // Render SV triangle (rotated according to hue) ImVec2 tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle); @@ -6523,25 +6573,18 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl return pressed; } -// Initialize/override default color options -// FIXME: Could be moved to a simple IO field. +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +// This allowed passing 0 to set default, whereas NewFrame() will assert if one of missing. Abide to old logic. void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags) { ImGuiContext& g = *GImGui; - if ((flags & ImGuiColorEditFlags_DisplayMask_) == 0) - flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DisplayMask_; - if ((flags & ImGuiColorEditFlags_DataTypeMask_) == 0) - flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DataTypeMask_; - if ((flags & ImGuiColorEditFlags_PickerMask_) == 0) - flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_PickerMask_; - if ((flags & ImGuiColorEditFlags_InputMask_) == 0) - flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_InputMask_; - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DisplayMask_)); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DataTypeMask_)); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_PickerMask_)); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_)); // Check only 1 option is selected - g.ColorEditOptions = flags; + if ((flags & ImGuiColorEditFlags_DisplayMask_) == 0) { flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DisplayMask_; } + if ((flags & ImGuiColorEditFlags_DataTypeMask_) == 0) { flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DataTypeMask_; } + if ((flags & ImGuiColorEditFlags_PickerMask_) == 0) { flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_PickerMask_; } + if ((flags & ImGuiColorEditFlags_InputMask_) == 0) { flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_InputMask_; } + g.IO.ConfigColorEditFlags = flags; } +#endif // Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags) @@ -6589,7 +6632,7 @@ void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) ImGuiContext& g = *GImGui; PushItemFlag(ImGuiItemFlags_NoMarkEdited, true); - ImGuiColorEditFlags opts = g.ColorEditOptions; + ImGuiColorEditFlags opts = g.IO.ConfigColorEditFlags; if (allow_opt_inputs) { if (RadioButton("RGB", (opts & ImGuiColorEditFlags_DisplayRGB) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayRGB; @@ -6629,7 +6672,7 @@ void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) EndPopup(); } - g.ColorEditOptions = opts; + g.IO.ConfigColorEditFlags = opts; PopItemFlag(); EndPopup(); } @@ -6657,7 +6700,7 @@ void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags fl if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel; ImVec2 backup_pos = GetCursorScreenPos(); if (Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup - g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags_PickerMask_) | (picker_flags & ImGuiColorEditFlags_PickerMask_); + g.IO.ConfigColorEditFlags = (g.IO.ConfigColorEditFlags & ~ImGuiColorEditFlags_PickerMask_) | (picker_flags & ImGuiColorEditFlags_PickerMask_); SetCursorScreenPos(backup_pos); ImVec4 previewing_ref_col; memcpy(&previewing_ref_col, ref_col, sizeof(float) * ((picker_flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4)); @@ -6669,7 +6712,7 @@ void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags fl if (allow_opt_alpha_bar) { if (allow_opt_picker) Separator(); - CheckboxFlags("Alpha Bar", &g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar); + CheckboxFlags("Alpha Bar", &g.IO.ConfigColorEditFlags, ImGuiColorEditFlags_AlphaBar); } PopItemFlag(); EndPopup(); @@ -7114,9 +7157,9 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l if (hovered || selected) { const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); - RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false); + RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false, 0.0f); } - RenderNavCursor(frame_bb, id, nav_render_cursor_flags); + RenderNavCursor(frame_bb, id, nav_render_cursor_flags, 0.0f); if (span_all_columns && !span_all_columns_label) TablePopBackgroundChannel(); if (flags & ImGuiTreeNodeFlags_Bullet) @@ -7487,6 +7530,11 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (selected != was_selected) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; + if (g.ActiveId == id && g.ActiveIdIsJustActivated) + { + g.ActiveIdWasSelected = was_selected; + g.ActiveIdWasSoleSelected = was_selected && (!is_multi_select || g.CurrentMultiSelect->IsSoleOrUnknownSelectionSize); + } // Render if (is_visible) @@ -7496,14 +7544,14 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl { // Between 1.91.0 and 1.91.4 we made selected Selectable use an arbitrary lerp between _Header and _HeaderHovered. Removed that now. (#8106) ImU32 col = GetColorU32((held && highlighted) ? ImGuiCol_HeaderActive : highlighted ? ImGuiCol_HeaderHovered : ImGuiCol_Header); - RenderFrame(bb.Min, bb.Max, col, false, 0.0f); + RenderFrame(bb.Min, bb.Max, col, false, style.SelectableRounding); } if (g.NavId == id) { - ImGuiNavRenderCursorFlags nav_render_cursor_flags = ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding; + ImGuiNavRenderCursorFlags nav_render_cursor_flags = ImGuiNavRenderCursorFlags_Compact; if (is_multi_select) nav_render_cursor_flags |= ImGuiNavRenderCursorFlags_AlwaysDraw; // Always show the nav rectangle - RenderNavCursor(bb, id, nav_render_cursor_flags); + RenderNavCursor(bb, id, nav_render_cursor_flags, style.SelectableRounding); } } @@ -8114,6 +8162,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int sel storage->LastSelectionSize = 0; } ms->LoopRequestSetAll = request_select_all ? 1 : request_clear ? 0 : -1; + ms->IsSoleOrUnknownSelectionSize = (storage->LastSelectionSize == 1) || (storage->LastSelectionSize == -1); //ms->PrevSubmittedItem = ImGuiSelectionUserData_Invalid; if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) @@ -8217,7 +8266,7 @@ void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_d if (ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect) { // Auto updating RangeSrcPassedBy for cases were clipper is not used (done before ItemAdd() clipping) - g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData | ImGuiItemFlags_IsMultiSelect; + g.NextItemData.ItemFlagsSet |= ImGuiItemFlags_HasSelectionUserData | ImGuiItemFlags_IsMultiSelect; if (ms->IO.RangeSrcItem == selection_user_data) ms->RangeSrcPassedBy = true; //ms->PrevSubmittedItem = ms->CurrSubmittedItem; // Can't rely on previous g.NextItemData.SelectionUserData because NextItemData is not restored on nested multi-select. @@ -8225,7 +8274,7 @@ void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_d } else { - g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData; + g.NextItemData.ItemFlagsSet |= ImGuiItemFlags_HasSelectionUserData; } } @@ -8305,7 +8354,7 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags // - Altering selection based on Ctrl/Shift modifiers, both for keyboard and mouse. // - Record current selection state for RangeSrc // This is all rather complex, best to run and refer to "widgets_multiselect_xxx" tests in imgui_test_suite. -void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) +void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed, ImGuiMultiSelectFlags extra_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -8325,7 +8374,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) ImGuiSelectionUserData item_data = g.NextItemData.SelectionUserData; - ImGuiMultiSelectFlags flags = ms->Flags; + ImGuiMultiSelectFlags flags = ms->Flags | extra_flags; const bool is_singleselect = (flags & ImGuiMultiSelectFlags_SingleSelect) != 0; bool is_ctrl = (ms->KeyMods & ImGuiMod_Ctrl) != 0; bool is_shift = (ms->KeyMods & ImGuiMod_Shift) != 0; @@ -8479,7 +8528,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) //IM_ASSERT(storage->HasRangeSrc && storage->HasRangeValue); if (storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) storage->RangeSrcItem = item_data; - if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0) + if ((flags & ImGuiMultiSelectFlags_CheckboxMode_) == 0) { // Shift+Arrow always select // Ctrl+Shift+Arrow copy source selection state (already stored by BeginMultiSelect() in storage->RangeSelected) @@ -8499,7 +8548,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) else { // Ctrl inverts selection, otherwise always select - if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0) + if ((flags & ImGuiMultiSelectFlags_CheckboxMode_) == 0) selected = is_ctrl ? !selected : true; else selected = !selected; @@ -8950,7 +8999,7 @@ int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_get // Tooltip on hover if (hovered && inner_bb.Contains(g.IO.MousePos)) { - const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f); + const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.999f); const int v_idx = (int)(t * item_count); IM_ASSERT(v_idx >= 0 && v_idx < values_count); @@ -9348,7 +9397,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) return false; ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; + ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); bool menu_is_open = IsPopupOpen(id, ImGuiPopupFlags_None); @@ -9380,7 +9429,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) // This is only done for items for the menu set and not the full parent window. const bool menuset_is_open = IsRootOfOpenMenuSet(); if (menuset_is_open) - PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck, true); + g.NextItemData.ItemFlagsSet |= ImGuiItemFlags_NoWindowHoverableCheck; // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu, // However the final position is going to be different! It is chosen by FindBestWindowPosForPopup(). @@ -9393,6 +9442,8 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) bool pressed; + const float backup_rounding = style.SelectableRounding; + style.SelectableRounding = style.MenuItemRounding; const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoAutoClosePopups | (ImGuiSelectableFlags)ImGuiSelectableFlags_SelectOnClick; ImGuiMenuColumns* offsets = &window->DC.MenuColumns; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) @@ -9428,6 +9479,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) RenderArrow(window->DrawList, ImVec2(text_pos.x + offsets->OffsetMark + extra_w + g.FontSize * 0.30f, text_pos.y), GetColorU32(ImGuiCol_Text), ImGuiDir_Right); popup_pos = ImVec2(pos.x, text_pos.y - style.WindowPadding.y); } + style.SelectableRounding = backup_rounding; if (!enabled) EndDisabled(); @@ -9440,8 +9492,6 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) } const bool hovered = (g.HoveredId == id) && enabled && !g.NavHighlightItemUnderNav; - if (menuset_is_open) - PopItemFlag(); bool want_open = false; bool want_open_nav_init = false; @@ -9604,7 +9654,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut // See BeginMenuEx() for comments about this. const bool menuset_is_open = IsRootOfOpenMenuSet(); if (menuset_is_open) - PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck, true); + g.NextItemData.ItemFlagsSet |= ImGuiItemFlags_NoWindowHoverableCheck; // We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73), // but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only. @@ -9614,6 +9664,8 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut BeginDisabled(); // We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another. + const float backup_rounding = style.SelectableRounding; + style.SelectableRounding = style.MenuItemRounding; const ImGuiSelectableFlags selectable_flags = (ImGuiSelectableFlags)ImGuiSelectableFlags_SelectOnRelease | (ImGuiSelectableFlags)ImGuiSelectableFlags_SetNavIdOnHover; ImGuiMenuColumns* offsets = &window->DC.MenuColumns; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) @@ -9636,7 +9688,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.) float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f; float shortcut_w = (shortcut && shortcut[0]) ? CalcTextSize(shortcut, NULL).x : 0.0f; - float checkmark_w = IM_TRUNC(g.FontSize * 1.20f); + float checkmark_w = IM_TRUNC(g.FontSize * 1.20f); // FIXME: Always accounted for, even in a menu with none, because 'selected' has no neutral setting. float min_w = offsets->DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); ImVec2 text_pos(pos.x, pos.y + window->DC.CurrLineTextBaseOffset); @@ -9657,6 +9709,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut RenderCheckMark(window->DrawList, text_pos + ImVec2(offsets->OffsetMark + stretch_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(ImGuiCol_Text), g.FontSize * 0.866f); } } + style.SelectableRounding = backup_rounding; // Once dragged, release ActiveId + key ownership. This is to allow the idiom of mouse down a menu, dragging elsewhere, up on some other MenuItem(). (#8233, #9394) // Could move logic into lower-level ImGuiButtonFlags_AutoReleaseActiveId + ImGuiButtonFlags_AutoReleaseKeyOwner? Easier once we get rid of the Selectable() middle-man here. @@ -9672,8 +9725,6 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut if (!enabled) EndDisabled(); PopID(); - if (menuset_is_open) - PopItemFlag(); return pressed; } @@ -10871,13 +10922,9 @@ void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabI IM_UNUSED(flags); IM_ASSERT(width > 0.0f); const float rounding = ImMax(0.0f, ImMin((flags & ImGuiTabItemFlags_Button) ? g.Style.FrameRounding : g.Style.TabRounding, width * 0.5f - 1.0f)); - const float y1 = bb.Min.y + 1.0f; + const float y1 = bb.Min.y + 1.0f; // Leave a bit of room in title bars. const float y2 = bb.Max.y - g.Style.TabBarBorderSize; - draw_list->PathLineTo(ImVec2(bb.Min.x, y2)); - draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding, y1 + rounding), rounding, 6, 9); - draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding, y1 + rounding), rounding, 9, 12); - draw_list->PathLineTo(ImVec2(bb.Max.x, y2)); - draw_list->PathFillConvex(col); + draw_list->AddRectFilled(bb.Min, ImVec2(bb.Max.x, y2), col, rounding, ImDrawFlags_RoundCornersTop); if (g.Style.TabBorderSize > 0.0f) { draw_list->PathLineTo(ImVec2(bb.Min.x + 0.5f, y2)); diff --git a/pcsx2/GS/Renderers/DX11/GSDevice11.cpp b/pcsx2/GS/Renderers/DX11/GSDevice11.cpp index 2339328b46..04728e6ddc 100644 --- a/pcsx2/GS/Renderers/DX11/GSDevice11.cpp +++ b/pcsx2/GS/Renderers/DX11/GSDevice11.cpp @@ -2381,7 +2381,7 @@ void GSDevice11::RenderImGui() { ImGui::Render(); const ImDrawData* draw_data = ImGui::GetDrawData(); - if (draw_data->CmdListsCount == 0) + if (draw_data->CmdLists.Size == 0) return; UpdateImGuiTextures(); @@ -2415,7 +2415,7 @@ void GSDevice11::RenderImGui() PSSetSamplerState(m_convert.ln.get()); // Render command lists - for (int n = 0; n < draw_data->CmdListsCount; n++) + for (int n = 0; n < draw_data->CmdLists.Size; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; diff --git a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp index 8541f266a5..78063130a8 100644 --- a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp +++ b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp @@ -2361,7 +2361,7 @@ void GSDevice12::RenderImGui() { ImGui::Render(); const ImDrawData* draw_data = ImGui::GetDrawData(); - if (draw_data->CmdListsCount == 0) + if (draw_data->CmdLists.Size == 0) return; UpdateImGuiTextures(); @@ -2402,7 +2402,7 @@ void GSDevice12::RenderImGui() // this is for presenting, we don't want to screw with the viewport/scissor set by display m_dirty_flags &= ~(DIRTY_FLAG_RENDER_TARGET | DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR); - for (int n = 0; n < draw_data->CmdListsCount; n++) + for (int n = 0; n < draw_data->CmdLists.Size; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; diff --git a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm index 6bf6af8ebf..b932c408da 100644 --- a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm +++ b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm @@ -2751,7 +2751,7 @@ static simd::float4 ToSimd(const ImVec4& vec) void GSDeviceMTL::RenderImGui(ImDrawData* data) { - if (data->CmdListsCount == 0) + if (data->CmdLists.Size == 0) return; UpdateImGuiTextures(); simd::float4 transform; @@ -2774,7 +2774,7 @@ void GSDeviceMTL::RenderImGui(ImDrawData* data) simd::float2 clip_scale = ToSimd(data->FramebufferScale); // (1,1) unless using retina display which are often (2,2) ImTextureID last_tex = reinterpret_cast(nullptr); - for (int i = 0; i < data->CmdListsCount; i++) + for (int i = 0; i < data->CmdLists.Size; i++) { const ImDrawList* cmd_list = data->CmdLists[i]; size_t vtx_size = cmd_list->VtxBuffer.Size * sizeof(ImDrawVert); diff --git a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp index e3b8f7b1c7..74765f6131 100644 --- a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp +++ b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp @@ -2506,7 +2506,7 @@ void GSDeviceOGL::RenderImGui() { ImGui::Render(); const ImDrawData* draw_data = ImGui::GetDrawData(); - if (draw_data->CmdListsCount == 0) + if (draw_data->CmdLists.Size == 0) return; UpdateImGuiTextures(); @@ -2537,7 +2537,7 @@ void GSDeviceOGL::RenderImGui() GSVector4i last_scissor = GSVector4i::xffffffff(); // Render command lists - for (int n = 0; n < draw_data->CmdListsCount; n++) + for (int n = 0; n < draw_data->CmdLists.Size; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; diff --git a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp index d3dad7e9c0..90db0fd3a7 100644 --- a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp +++ b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp @@ -4645,7 +4645,7 @@ void GSDeviceVK::RenderImGui() { ImGui::Render(); const ImDrawData* draw_data = ImGui::GetDrawData(); - if (draw_data->CmdListsCount == 0) + if (draw_data->CmdLists.Size == 0) return; UpdateImGuiTextures(); @@ -4668,7 +4668,7 @@ void GSDeviceVK::RenderImGui() // this is for presenting, we don't want to screw with the viewport/scissor set by display m_dirty_flags &= ~(DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR); - for (int n = 0; n < draw_data->CmdListsCount; n++) + for (int n = 0; n < draw_data->CmdLists.Size; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n];