diff --git a/dep/imgui/include/imconfig.h b/dep/imgui/include/imconfig.h index 481140451..14f66b2d7 100644 --- a/dep/imgui/include/imconfig.h +++ b/dep/imgui/include/imconfig.h @@ -15,7 +15,8 @@ #pragma once //---- Define assertion handler. Defaults to calling assert(). -// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement. +// - If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement. +// - Compiling with NDEBUG will usually strip out assert() to nothing, which is NOT recommended because we use asserts to notify of programmer mistakes. //#define IM_ASSERT(_EXPR) MyAssert(_EXPR) //#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts @@ -83,6 +84,7 @@ //---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui) // Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided). +// Note that imgui_freetype.cpp may be used _without_ this define, if you manually call ImFontAtlas::SetFontLoader(). The define is simply a convenience. // On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'. #define IMGUI_ENABLE_FREETYPE @@ -129,6 +131,10 @@ //#define IM_DEBUG_BREAK IM_ASSERT(0) //#define IM_DEBUG_BREAK __debugbreak() +//---- Debug Tools: Enable highlight ID conflicts _before_ hovering items. When io.ConfigDebugHighlightIdConflicts is set. +// (THIS WILL SLOW DOWN DEAR IMGUI. Only use occasionally and disable after use) +//#define IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS + //---- Debug Tools: Enable slower asserts //#define IMGUI_DEBUG_PARANOID diff --git a/dep/imgui/include/imgui.h b/dep/imgui/include/imgui.h index 7de72ab53..0d5250b59 100644 --- a/dep/imgui/include/imgui.h +++ b/dep/imgui/include/imgui.h @@ -1,35 +1,36 @@ -// dear imgui, v1.92.0 +// dear imgui, v1.92.6 WIP // (headers) // Help: -// - See links below. // - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that. // - Read top of imgui.cpp for more details, links and comments. -// - Add '#define IMGUI_DEFINE_MATH_OPERATORS' before including this file (or in imconfig.h) to access courtesy maths operators for ImVec2 and ImVec4. +// - Add '#define IMGUI_DEFINE_MATH_OPERATORS' before including imgui.h (or in imconfig.h) to access courtesy maths operators for ImVec2 and ImVec4. // Resources: // - FAQ ........................ https://dearimgui.com/faq (in repository as docs/FAQ.md) // - Homepage ................... https://github.com/ocornut/imgui -// - Releases & changelog ....... https://github.com/ocornut/imgui/releases +// - Releases & Changelog ....... https://github.com/ocornut/imgui/releases // - Gallery .................... https://github.com/ocornut/imgui/issues?q=label%3Agallery (please post your screenshots/video there!) // - Wiki ....................... https://github.com/ocornut/imgui/wiki (lots of good stuff there) // - Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started (how to integrate in an existing app by adding ~25 lines of code) // - Third-party Extensions https://github.com/ocornut/imgui/wiki/Useful-Extensions (ImPlot & many more) -// - Bindings/Backends https://github.com/ocornut/imgui/wiki/Bindings (language bindings, backends for various tech/engines) -// - Glossary https://github.com/ocornut/imgui/wiki/Glossary +// - Bindings/Backends https://github.com/ocornut/imgui/wiki/Bindings (language bindings + backends for various tech/engines) // - Debug Tools https://github.com/ocornut/imgui/wiki/Debug-Tools +// - Glossary https://github.com/ocornut/imgui/wiki/Glossary // - Software using Dear ImGui https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui // - Issues & support ........... https://github.com/ocornut/imgui/issues // - Test Engine & Automation ... https://github.com/ocornut/imgui_test_engine (test suite, test engine to automate your apps) +// - Web version of the Demo .... https://pthom.github.io/imgui_manual_online/manual/imgui_manual.html (w/ source code browser) -// For first-time users having issues compiling/linking/running/loading fonts: +// For FIRST-TIME users having issues compiling/linking/running: // please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above. -// Everything else should be asked in 'Issues'! We are building a database of cross-linked knowledge there. +// EVERYTHING ELSE should be asked in 'Issues'! We are building a database of cross-linked knowledge there. +// Since 1.92, we encourage font loading questions to also be posted in 'Issues'. // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') -#define IMGUI_VERSION "1.92.0" -#define IMGUI_VERSION_NUM 19200 +#define IMGUI_VERSION "1.92.6 WIP" +#define IMGUI_VERSION_NUM 19258 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 @@ -89,19 +90,24 @@ Index of this file: #endif // Helper Macros +// (note: compiling with NDEBUG will usually strip out assert() to nothing, which is NOT recommended because we use asserts to notify of programmer mistakes.) #ifndef IM_ASSERT #include #define IM_ASSERT(_EXPR) assert(_EXPR) // You can override the default assert handler by editing imconfig.h #endif -#define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR) / sizeof(*(_ARR)))) // Size of a static C-style array. Don't use on pointers! +#define IM_COUNTOF(_ARR) ((int)(sizeof(_ARR) / sizeof(*(_ARR)))) // Size of a static C-style array. Don't use on pointers! #define IM_UNUSED(_VAR) ((void)(_VAR)) // Used to silence "unused variable warnings". Often useful as asserts may be stripped out from final builds. +#define IM_STRINGIFY_HELPER(_EXPR) #_EXPR +#define IM_STRINGIFY(_EXPR) IM_STRINGIFY_HELPER(_EXPR) // Preprocessor idiom to stringify e.g. an integer or a macro. // Check that version and structures layouts are matching between compiled imgui code and caller. Read comments above DebugCheckVersionAndDataLayout() for details. #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) // Helper Macros - IM_FMTARGS, IM_FMTLIST: Apply printf-style warnings to our formatting functions. -// (MSVC provides an equivalent mechanism via SAL Annotations but it would require the macros in a different -// location. e.g. #include + void myprintf(_Printf_format_string_ const char* format, ...)) +// (MSVC provides an equivalent mechanism via SAL Annotations but it requires the macros in a different +// location. e.g. #include + void myprintf(_Printf_format_string_ const char* format, ...), +// and only works when using Code Analysis, rather than just normal compiling). +// (see https://github.com/ocornut/imgui/issues/8871 for a patch to enable this for MSVC's Code Analysis) #if !defined(IMGUI_USE_STB_SPRINTF) && defined(__MINGW32__) && !defined(__clang__) #define IM_FMTARGS(FMT) __attribute__((format(gnu_printf, FMT, FMT+1))) #define IM_FMTLIST(FMT) __attribute__((format(gnu_printf, FMT, 0))) @@ -209,9 +215,9 @@ struct ImGuiViewport; // A Platform Window (always only one in 'ma // Enumerations // - We don't use strongly typed enums much because they add constraints (can't extend in private code, can't store typed in bit fields, extra casting on iteration) // - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists! -// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. -// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments. -// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments. +// - In Visual Studio: Ctrl+Comma ("Edit.GoToAll") can follow symbols inside comments, whereas Ctrl+F12 ("Edit.GoToImplementation") cannot. +// - In Visual Studio w/ Visual Assist installed: Alt+G ("VAssistX.GoToImplementation") can also follow symbols inside comments. +// - In VS Code, CLion, etc.: Ctrl+Click can follow symbols inside comments. enum ImGuiDir : int; // -> enum ImGuiDir // Enum: A cardinal direction (Left, Right, Up, Down) enum ImGuiKey : int; // -> enum ImGuiKey // Enum: A key identifier (ImGuiKey_XXX or ImGuiMod_XXX value) enum ImGuiMouseSource : int; // -> enum ImGuiMouseSource // Enum; A mouse input source identifier (Mouse, TouchScreen, Pen) @@ -226,11 +232,12 @@ typedef int ImGuiTableBgTarget; // -> enum ImGuiTableBgTarget_ // Enum: A // Flags (declared as int to allow using as flags without overhead, and to not pollute the top of this file) // - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists! -// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. -// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments. -// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments. +// - In Visual Studio: Ctrl+Comma ("Edit.GoToAll") can follow symbols inside comments, whereas Ctrl+F12 ("Edit.GoToImplementation") cannot. +// - In Visual Studio w/ Visual Assist installed: Alt+G ("VAssistX.GoToImplementation") can also follow symbols inside comments. +// - In VS Code, CLion, etc.: Ctrl+Click can follow symbols inside comments. typedef int ImDrawFlags; // -> enum ImDrawFlags_ // Flags: for ImDrawList functions typedef int ImDrawListFlags; // -> enum ImDrawListFlags_ // Flags: for ImDrawList instance +typedef int ImDrawTextFlags; // -> enum ImDrawTextFlags_ // Internal, do not use! typedef int ImFontFlags; // -> enum ImFontFlags_ // Flags: for ImFont typedef int ImFontAtlasFlags; // -> enum ImFontAtlasFlags_ // Flags: for ImFontAtlas typedef int ImGuiBackendFlags; // -> enum ImGuiBackendFlags_ // Flags: for io.BackendFlags @@ -246,6 +253,7 @@ typedef int ImGuiInputFlags; // -> enum ImGuiInputFlags_ // Flags: f typedef int ImGuiInputTextFlags; // -> enum ImGuiInputTextFlags_ // Flags: for InputText(), InputTextMultiline() typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag(), shared by all items typedef int ImGuiKeyChord; // -> ImGuiKey | ImGuiMod_XXX // Flags: for IsKeyChordPressed(), Shortcut() etc. an ImGuiKey optionally OR-ed with one or more ImGuiMod_XXX values. +typedef int ImGuiListClipperFlags; // -> enum ImGuiListClipperFlags_// Flags: for ImGuiListClipper typedef int ImGuiPopupFlags; // -> enum ImGuiPopupFlags_ // Flags: for OpenPopup*(), BeginPopupContext*(), IsPopupOpen() typedef int ImGuiMultiSelectFlags; // -> enum ImGuiMultiSelectFlags_// Flags: for BeginMultiSelect() typedef int ImGuiSelectableFlags; // -> enum ImGuiSelectableFlags_ // Flags: for Selectable() @@ -317,7 +325,7 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE // - When a Rendered Backend creates a texture, it store its native identifier into a ImTextureID value. // (e.g. Used by DX11 backend to a `ID3D11ShaderResourceView*`; Used by OpenGL backends to store `GLuint`; // Used by SDLGPU backend to store a `SDL_GPUTextureSamplerBinding*`, etc.). -// - User may submit their own textures to e.g. ImGui::Image() function by passing the same type. +// - User may submit their own textures to e.g. ImGui::Image() function by passing this value. // - During the rendering loop, the Renderer Backend retrieve the ImTextureID, which stored inside a // ImTextureRef, which is stored inside a ImDrawCmd. // - Compile-time type configuration: @@ -326,8 +334,8 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE // - You may decide to store a higher-level structure containing texture, sampler, shader etc. with various // constructors if you like. You will need to implement ==/!= operators. // History: -// - In v1.91.4 (2024/10/08): the default type for ImTextureID was changed from 'void*' to 'ImU64'. This allowed backends requirig 64-bit worth of data to build on 32-bit architectures. Use intermediary intptr_t cast and read FAQ if you have casting warnings. -// - In v1.92.0 (2025/XX/XX): added ImTextureRef which carry either a ImTextureID either a pointer to internal texture atlas. All user facing functions taking ImTextureID changed to ImTextureRef +// - In v1.91.4 (2024/10/08): the default type for ImTextureID was changed from 'void*' to 'ImU64'. This allowed backends requiring 64-bit worth of data to build on 32-bit architectures. Use intermediary intptr_t cast and read FAQ if you have casting warnings. +// - In v1.92.0 (2025/06/11): added ImTextureRef which carry either a ImTextureID either a pointer to internal texture atlas. All user facing functions taking ImTextureID changed to ImTextureRef #ifndef ImTextureID typedef ImU64 ImTextureID; // Default: store up to 64-bits (any pointer or integer). A majority of backends are ok with that. #endif @@ -337,15 +345,17 @@ typedef ImU64 ImTextureID; // Default: store up to 64-bits (any pointer or #define ImTextureID_Invalid ((ImTextureID)0) #endif -// ImTextureRef = higher-level identifier for a texture. +// ImTextureRef = higher-level identifier for a texture. Store a ImTextureID _or_ a ImTextureData*. // The identifier is valid even before the texture has been uploaded to the GPU/graphics system. // This is what gets passed to functions such as `ImGui::Image()`, `ImDrawList::AddImage()`. // This is what gets stored in draw commands (`ImDrawCmd`) to identify a texture during rendering. -// - When a texture is created by user code (e.g. custom images), we directly stores the low-level ImTextureID. +// - When a texture is created by user code (e.g. custom images), we directly store the low-level ImTextureID. +// Because of this, when displaying your own texture you are likely to ever only manage ImTextureID values on your side. // - When a texture is created by the backend, we stores a ImTextureData* which becomes an indirection // to extract the ImTextureID value during rendering, after texture upload has happened. -// - There is no constructor to create a ImTextureID from a ImTextureData* as we don't expect this -// to be useful to the end-user, and it would be erroneously called by many legacy code. +// - To create a ImTextureRef from a ImTextureData you can use ImTextureData::GetTexRef(). +// We intentionally do not provide an ImTextureRef constructor for this: we don't expect this +// to be frequently useful to the end-user, and it would be erroneously called by many legacy code. // - If you want to bind the current atlas when using custom rectangle, you can use io.Fonts->TexRef. // - Binding generators for languages such as C (which don't have constructors), should provide a helper, e.g. // inline ImTextureRef ImTextureRefFromID(ImTextureID tex_id) { ImTextureRef tex_ref = { ._TexData = NULL, .TexID = tex_id }; return tex_ref; } @@ -654,13 +664,13 @@ namespace ImGui IMGUI_API bool Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int popup_max_height_in_items = -1); // Widgets: Drag Sliders - // - CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp. + // - Ctrl+Click on any drag box to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp. // - For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every function, note that a 'float v[X]' function argument is the same as 'float* v', // the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. // - Format string may also be set to NULL or use the default format ("%f" or "%d"). // - Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For keyboard/gamepad navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision). - // - Use v_min < v_max to clamp edits to given limits. Note that CTRL+Click manual input can override those limits if ImGuiSliderFlags_AlwaysClamp is not used. + // - Use v_min < v_max to clamp edits to given limits. Note that Ctrl+Click manual input can override those limits if ImGuiSliderFlags_AlwaysClamp is not used. // - Use v_max = FLT_MAX / INT_MAX etc to avoid clamping to a maximum, same with v_min = -FLT_MAX / INT_MIN to avoid clamping to a minimum. // - We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them. // - Legacy: Pre-1.78 there are DragXXX() function signatures that take a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument. @@ -679,7 +689,7 @@ namespace ImGui IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed = 1.0f, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0); // Widgets: Regular Sliders - // - CTRL+Click on any slider to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp. + // - Ctrl+Click on any slider to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp. // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. // - Format string may also be set to NULL or use the default format ("%f" or "%d"). // - Legacy: Pre-1.78 there are SliderXXX() function signatures that take a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument. @@ -700,7 +710,7 @@ namespace ImGui IMGUI_API bool VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format = NULL, ImGuiSliderFlags flags = 0); // Widgets: Input with Keyboard - // - If you want to use InputText() with std::string or any custom dynamic string type, see misc/cpp/imgui_stdlib.h and comments in imgui_demo.cpp. + // - If you want to use InputText() with std::string or any custom dynamic string type, use the wrapper in misc/cpp/imgui_stdlib.h/.cpp! // - Most of the ImGuiInputTextFlags flags are only useful for InputText() and not for InputFloatX, InputIntX, InputDouble etc. IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); IMGUI_API bool InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); @@ -730,7 +740,7 @@ namespace ImGui // 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. IMGUI_API bool TreeNode(const char* label); - IMGUI_API bool TreeNode(const char* str_id, const char* fmt, ...) IM_FMTARGS(2); // helper variation to easily decorelate the id from the displayed string. Read the FAQ about why and how to use ID. to align arbitrary text at the same level as a TreeNode() you can use Bullet(). + IMGUI_API bool TreeNode(const char* str_id, const char* fmt, ...) IM_FMTARGS(2); // helper variation to easily decorrelate the id from the displayed string. Read the FAQ about why and how to use ID. to align arbitrary text at the same level as a TreeNode() you can use Bullet(). IMGUI_API bool TreeNode(const void* ptr_id, const char* fmt, ...) IM_FMTARGS(2); // " IMGUI_API bool TreeNodeV(const char* str_id, const char* fmt, va_list args) IM_FMTLIST(2); IMGUI_API bool TreeNodeV(const void* ptr_id, const char* fmt, va_list args) IM_FMTLIST(2); @@ -755,7 +765,7 @@ namespace ImGui IMGUI_API bool Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // "bool* p_selected" point to the selection state (read-write), as a convenient helper. // Multi-selection system for Selectable(), Checkbox(), TreeNode() functions [BETA] - // - This enables standard multi-selection/range-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc.) in a way that also allow a clipper to be used. + // - This enables standard multi-selection/range-selection idioms (Ctrl+Mouse/Keyboard, Shift+Mouse/Keyboard, etc.) in a way that also allow a clipper to be used. // - ImGuiSelectionUserData is often used to store your item index within the current view (but may store something else). // - Read comments near ImGuiMultiSelectIO for instructions/details and see 'Demo->Widgets->Selection State & Multi-Select' for demo. // - TreeNode() is technically supported but... using this correctly is more complicated. You need some sort of linear/random access to your tree, @@ -888,7 +898,7 @@ namespace ImGui // - 5. Call EndTable() IMGUI_API bool BeginTable(const char* str_id, int columns, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0.0f, 0.0f), float inner_width = 0.0f); IMGUI_API void EndTable(); // only call EndTable() if BeginTable() returns true! - IMGUI_API void TableNextRow(ImGuiTableRowFlags row_flags = 0, float min_row_height = 0.0f); // append into the first cell of a new row. + IMGUI_API void TableNextRow(ImGuiTableRowFlags row_flags = 0, float min_row_height = 0.0f); // append into the first cell of a new row. 'min_row_height' include the minimum top and bottom padding aka CellPadding.y * 2.0f. IMGUI_API bool TableNextColumn(); // append into the next column (or first column of next row if currently in last column). Return true when column is visible. IMGUI_API bool TableSetColumnIndex(int column_n); // append into the specified column. Return true when column is visible. @@ -915,7 +925,7 @@ namespace ImGui IMGUI_API ImGuiTableSortSpecs* TableGetSortSpecs(); // get latest sort specs for the table (NULL if not sorting). Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable(). IMGUI_API int TableGetColumnCount(); // return number of columns (value passed to BeginTable) IMGUI_API int TableGetColumnIndex(); // return current column index. - IMGUI_API int TableGetRowIndex(); // return current row index. + IMGUI_API int TableGetRowIndex(); // return current row index (header rows are accounted for) IMGUI_API const char* TableGetColumnName(int column_n = -1); // return "" if column didn't have a name declared by TableSetupColumn(). Pass -1 to use current column. IMGUI_API ImGuiTableColumnFlags TableGetColumnFlags(int column_n = -1); // return column flags so you can query their Enabled/Visible/Sorted/Hovered status flags. Pass -1 to use current column. IMGUI_API void TableSetColumnEnabled(int column_n, bool v);// change user accessible enabled/disabled state of a column. Set to false to hide the column. User can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody) @@ -968,7 +978,7 @@ namespace ImGui // Disabling [BETA API] // - Disable all user interactions and dim items visuals (applying style.DisabledAlpha over current colors) // - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled) - // - Tooltips windows by exception are opted out of disabling. + // - Tooltips windows are automatically opted out of disabling. Note that IsItemHovered() by default returns false on disabled items, unless using ImGuiHoveredFlags_AllowWhenDisabled. // - BeginDisabled(false)/EndDisabled() essentially does nothing but is provided to facilitate use of boolean expressions (as a micro-optimization: if you have tens of thousands of BeginDisabled(false)/EndDisabled() pairs, you might want to reformulate your code to avoid making those calls) IMGUI_API void BeginDisabled(bool disabled = true); IMGUI_API void EndDisabled(); @@ -1008,6 +1018,7 @@ namespace ImGui IMGUI_API ImVec2 GetItemRectMin(); // get upper-left bounding rectangle of the last item (screen space) 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 // Viewports // - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows. @@ -1052,8 +1063,8 @@ namespace ImGui // Inputs Utilities: Shortcut Testing & Routing [BETA] // - ImGuiKeyChord = a ImGuiKey + optional ImGuiMod_Alt/ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Super. - // ImGuiKey_C // Accepted by functions taking ImGuiKey or ImGuiKeyChord arguments) - // ImGuiMod_Ctrl | ImGuiKey_C // Accepted by functions taking ImGuiKeyChord arguments) + // ImGuiKey_C // Accepted by functions taking ImGuiKey or ImGuiKeyChord arguments + // ImGuiMod_Ctrl | ImGuiKey_C // Accepted by functions taking ImGuiKeyChord arguments // only ImGuiMod_XXX values are legal to combine with an ImGuiKey. You CANNOT combine two ImGuiKey values. // - The general idea is that several callers may register interest in a shortcut, and only one owner gets it. // Parent -> call Shortcut(Ctrl+S) // When Parent is focused, Parent gets the shortcut. @@ -1160,7 +1171,7 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_AlwaysVerticalScrollbar= 1 << 14, // Always show vertical scrollbar (even if ContentSize.y < Size.y) ImGuiWindowFlags_AlwaysHorizontalScrollbar=1<< 15, // Always show horizontal scrollbar (even if ContentSize.x < Size.x) ImGuiWindowFlags_NoNavInputs = 1 << 16, // No keyboard/gamepad navigation within the window - ImGuiWindowFlags_NoNavFocus = 1 << 17, // No focusing toward this window with keyboard/gamepad navigation (e.g. skipped by CTRL+TAB) + ImGuiWindowFlags_NoNavFocus = 1 << 17, // No focusing toward this window with keyboard/gamepad navigation (e.g. skipped by Ctrl+Tab) ImGuiWindowFlags_UnsavedDocument = 1 << 18, // Display a dot next to the title. When used in a tab/docking context, tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, ImGuiWindowFlags_NoDecoration = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse, @@ -1175,13 +1186,13 @@ enum ImGuiWindowFlags_ // Obsolete names #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - ImGuiWindowFlags_NavFlattened = 1 << 29, // Obsoleted in 1.90.9: Use ImGuiChildFlags_NavFlattened in BeginChild() call. - ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 30, // Obsoleted in 1.90.0: Use ImGuiChildFlags_AlwaysUseWindowPadding in BeginChild() call. + //ImGuiWindowFlags_NavFlattened = 1 << 29, // Obsoleted in 1.90.9: moved to ImGuiChildFlags. BeginChild(name, size, 0, ImGuiWindowFlags_NavFlattened) --> BeginChild(name, size, ImGuiChildFlags_NavFlattened, 0) + //ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 30, // Obsoleted in 1.90.0: moved to ImGuiChildFlags. BeginChild(name, size, 0, ImGuiWindowFlags_AlwaysUseWindowPadding) --> BeginChild(name, size, ImGuiChildFlags_AlwaysUseWindowPadding, 0) #endif }; // Flags for ImGui::BeginChild() -// (Legacy: bit 0 must always correspond to ImGuiChildFlags_Borders to be backward compatible with old API using 'bool border = false'. +// (Legacy: bit 0 must always correspond to ImGuiChildFlags_Borders to be backward compatible with old API using 'bool border = false'.) // About using AutoResizeX/AutoResizeY flags: // - May be combined with SetNextWindowSizeConstraints() to set a min/max size for each axis (see "Demo->Child->Auto-resize with Constraints"). // - Size measurement for a given axis is only performed when the child window is within visible boundaries, or is just appearing. @@ -1204,12 +1215,12 @@ enum ImGuiChildFlags_ // Obsolete names #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - ImGuiChildFlags_Border = ImGuiChildFlags_Borders, // Renamed in 1.91.1 (August 2024) for consistency. + //ImGuiChildFlags_Border = ImGuiChildFlags_Borders, // Renamed in 1.91.1 (August 2024) for consistency. #endif }; // Flags for ImGui::PushItemFlag() -// (Those are shared by all items) +// (Those are shared by all submitted items) enum ImGuiItemFlags_ { ImGuiItemFlags_None = 0, // (Default) @@ -1219,6 +1230,7 @@ enum ImGuiItemFlags_ 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(). }; // Flags for ImGui::InputText() @@ -1260,6 +1272,15 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_CallbackResize = 1 << 22, // Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this) ImGuiInputTextFlags_CallbackEdit = 1 << 23, // Callback on any edit. Note that InputText() already returns true on edit + you can always use IsItemEdited(). The callback is useful to manipulate the underlying buffer while focus is active. + // Multi-line Word-Wrapping [BETA] + // - Not well tested yet. Please report any incorrect cursor movement, selection behavior etc. bug to https://github.com/ocornut/imgui/issues/3237. + // - Wrapping style is not ideal. Wrapping of long words/sections (e.g. words larger than total available width) may be particularly unpleasing. + // - Wrapping width needs to always account for the possibility of a vertical scrollbar. + // - It is much slower than regular text fields. + // Ballpark estimate of cost on my 2019 desktop PC: for a 100 KB text buffer: +~0.3 ms (Optimized) / +~1.0 ms (Debug build). + // The CPU cost is very roughly proportional to text length, so a 10 KB buffer should cost about ten times less. + ImGuiInputTextFlags_WordWrap = 1 << 24, // InputTextMultiline(): word-wrap lines that are too long. + // Obsolete names //ImGuiInputTextFlags_AlwaysInsertMode = ImGuiInputTextFlags_AlwaysOverwrite // [renamed in 1.82] name was not matching behavior }; @@ -1285,7 +1306,7 @@ enum ImGuiTreeNodeFlags_ ImGuiTreeNodeFlags_SpanAllColumns = 1 << 14, // Frame will span all columns of its container table (label will still fit in current column) ImGuiTreeNodeFlags_LabelSpanAllColumns = 1 << 15, // Label will span all columns of its container table //ImGuiTreeNodeFlags_NoScrollOnOpen = 1 << 16, // FIXME: TODO: Disable automatic scroll on TreePop() if node got just open and contents is not visible - ImGuiTreeNodeFlags_NavLeftJumpsToParent = 1 << 17, // Nav: left arrow moves back to parent. This is processed in TreePop() when there's an unfullfilled Left nav request remaining. + ImGuiTreeNodeFlags_NavLeftJumpsToParent = 1 << 17, // Nav: left arrow moves back to parent. This is processed in TreePop() when there's an unfulfilled Left nav request remaining. ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_NoAutoOpenOnLog, // [EXPERIMENTAL] Draw lines connecting TreeNode hierarchy. Discuss in GitHub issue #2920. @@ -1297,7 +1318,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_AllowItemOverlap = ImGuiTreeNodeFlags_AllowOverlap, // Renamed in 1.89.7 + //ImGuiTreeNodeFlags_AllowItemOverlap = ImGuiTreeNodeFlags_AllowOverlap, // Renamed in 1.89.7 #endif }; @@ -1336,10 +1357,11 @@ enum ImGuiSelectableFlags_ ImGuiSelectableFlags_Disabled = 1 << 3, // Cannot be selected, display grayed out text ImGuiSelectableFlags_AllowOverlap = 1 << 4, // (WIP) Hit testing to allow subsequent widgets to overlap this one ImGuiSelectableFlags_Highlight = 1 << 5, // Make the item be displayed as if it is hovered + ImGuiSelectableFlags_SelectOnNav = 1 << 6, // Auto-select when moved into, unless Ctrl is held. Automatic when in a BeginMultiSelect() block. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImGuiSelectableFlags_DontClosePopups = ImGuiSelectableFlags_NoAutoClosePopups, // Renamed in 1.91.0 - ImGuiSelectableFlags_AllowItemOverlap = ImGuiSelectableFlags_AllowOverlap, // Renamed in 1.89.7 + //ImGuiSelectableFlags_AllowItemOverlap = ImGuiSelectableFlags_AllowOverlap, // Renamed in 1.89.7 #endif }; @@ -1369,10 +1391,17 @@ enum ImGuiTabBarFlags_ ImGuiTabBarFlags_NoTabListScrollingButtons = 1 << 4, // Disable scrolling buttons (apply when fitting policy is ImGuiTabBarFlags_FittingPolicyScroll) ImGuiTabBarFlags_NoTooltip = 1 << 5, // Disable tooltips when hovering a tab ImGuiTabBarFlags_DrawSelectedOverline = 1 << 6, // Draw selected overline markers over selected tab - ImGuiTabBarFlags_FittingPolicyResizeDown = 1 << 7, // Resize tabs when they don't fit - ImGuiTabBarFlags_FittingPolicyScroll = 1 << 8, // Add scroll buttons when tabs don't fit - ImGuiTabBarFlags_FittingPolicyMask_ = ImGuiTabBarFlags_FittingPolicyResizeDown | ImGuiTabBarFlags_FittingPolicyScroll, - ImGuiTabBarFlags_FittingPolicyDefault_ = ImGuiTabBarFlags_FittingPolicyResizeDown, + + // Fitting/Resize policy + ImGuiTabBarFlags_FittingPolicyMixed = 1 << 7, // Shrink down tabs when they don't fit, until width is style.TabMinWidthShrink, then enable scrolling buttons. + ImGuiTabBarFlags_FittingPolicyShrink = 1 << 8, // Shrink down tabs when they don't fit + ImGuiTabBarFlags_FittingPolicyScroll = 1 << 9, // Enable scrolling buttons when tabs don't fit + ImGuiTabBarFlags_FittingPolicyMask_ = ImGuiTabBarFlags_FittingPolicyMixed | ImGuiTabBarFlags_FittingPolicyShrink | ImGuiTabBarFlags_FittingPolicyScroll, + ImGuiTabBarFlags_FittingPolicyDefault_ = ImGuiTabBarFlags_FittingPolicyMixed, + +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + ImGuiTabBarFlags_FittingPolicyResizeDown = ImGuiTabBarFlags_FittingPolicyShrink, // Renamed in 1.92.2 +#endif }; // Flags for ImGui::BeginTabItem() @@ -1427,7 +1456,7 @@ enum ImGuiHoveredFlags_ // Tooltips mode // - typically used in IsItemHovered() + SetTooltip() sequence. // - this is a shortcut to pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' where you can reconfigure desired behavior. - // e.g. 'TooltipHoveredFlagsForMouse' defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort'. + // e.g. 'HoverFlagsForTooltipMouse' defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_AllowWhenDisabled'. // - for frequently actioned or hovered items providing a tooltip, you want may to use ImGuiHoveredFlags_ForTooltip (stationary + delay) so the tooltip doesn't show too often. // - for items which main purpose is to be hovered, or items with low affordance, or in less consistent apps, prefer no delay or shorter delay. ImGuiHoveredFlags_ForTooltip = 1 << 12, // Shortcut for standard flags when using IsItemHovered() + SetTooltip() sequence. @@ -1459,6 +1488,7 @@ enum ImGuiDragDropFlags_ ImGuiDragDropFlags_AcceptBeforeDelivery = 1 << 10, // AcceptDragDropPayload() will returns true even before the mouse button is released. You can then call IsDelivery() to test if the payload needs to be delivered. ImGuiDragDropFlags_AcceptNoDrawDefaultRect = 1 << 11, // Do not draw the default highlight rectangle when hovering over target. ImGuiDragDropFlags_AcceptNoPreviewTooltip = 1 << 12, // Request hiding the BeginDragDropSource tooltip from the BeginDragDropTarget site. + ImGuiDragDropFlags_AcceptDrawAsHovered = 1 << 13, // Accepting item will render as if hovered. Useful for e.g. a Button() used as a drop target. ImGuiDragDropFlags_AcceptPeekOnly = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect, // For peeking ahead and inspecting the payload before delivery. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS @@ -1632,9 +1662,9 @@ enum ImGuiKey : int ImGuiMod_Mask_ = 0xF000, // 4-bits #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - ImGuiKey_COUNT = ImGuiKey_NamedKey_END, // Obsoleted in 1.91.5 because it was extremely misleading (since named keys don't start at 0 anymore) + ImGuiKey_COUNT = ImGuiKey_NamedKey_END, // Obsoleted in 1.91.5 because it was misleading (since named keys don't start at 0 anymore) ImGuiMod_Shortcut = ImGuiMod_Ctrl, // Removed in 1.90.7, you can now simply use ImGuiMod_Ctrl - ImGuiKey_ModCtrl = ImGuiMod_Ctrl, ImGuiKey_ModShift = ImGuiMod_Shift, ImGuiKey_ModAlt = ImGuiMod_Alt, ImGuiKey_ModSuper = ImGuiMod_Super, // Renamed in 1.89 + //ImGuiKey_ModCtrl = ImGuiMod_Ctrl, ImGuiKey_ModShift = ImGuiMod_Shift, ImGuiKey_ModAlt = ImGuiMod_Alt, ImGuiKey_ModSuper = ImGuiMod_Super, // Renamed in 1.89 //ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter, // Renamed in 1.87 #endif }; @@ -1656,7 +1686,7 @@ enum ImGuiInputFlags_ ImGuiInputFlags_RouteAlways = 1 << 13, // Do not register route, poll keys directly. // - Routing options ImGuiInputFlags_RouteOverFocused = 1 << 14, // Option: global route: higher priority than focused route (unless active item in focused route). - ImGuiInputFlags_RouteOverActive = 1 << 15, // Option: global route: higher priority than active item. Unlikely you need to use that: will interfere with every active items, e.g. CTRL+A registered by InputText will be overridden by this. May not be fully honored as user/internal code is likely to always assume they can access keys when active. + ImGuiInputFlags_RouteOverActive = 1 << 15, // Option: global route: higher priority than active item. Unlikely you need to use that: will interfere with every active items, e.g. Ctrl+A registered by InputText will be overridden by this. May not be fully honored as user/internal code is likely to always assume they can access keys when active. ImGuiInputFlags_RouteUnlessBgFocused = 1 << 16, // Option: global route: will not be applied if underlying background/void is focused (== no Dear ImGui windows are focused). Useful for overlay applications. ImGuiInputFlags_RouteFromRootWindow = 1 << 17, // Option: route evaluated from the point of view of root window rather than current window. @@ -1692,7 +1722,7 @@ enum ImGuiBackendFlags_ ImGuiBackendFlags_HasMouseCursors = 1 << 1, // Backend Platform supports honoring GetMouseCursor() value to change the OS cursor shape. ImGuiBackendFlags_HasSetMousePos = 1 << 2, // Backend Platform supports io.WantSetMousePos requests to reposition the OS mouse position (only used if io.ConfigNavMoveSetMousePos is set). ImGuiBackendFlags_RendererHasVtxOffset = 1 << 3, // Backend Renderer supports ImDrawCmd::VtxOffset. This enables output of large meshes (64K+ vertices) while still using 16-bit indices. - ImGuiBackendFlags_RendererHasTextures = 1 << 4, // Backend Renderer supports ImTextureData requests to create/update/destroy textures. This enables incremental texture updates and texture reloads. + ImGuiBackendFlags_RendererHasTextures = 1 << 4, // Backend Renderer supports ImTextureData requests to create/update/destroy textures. This enables incremental texture updates and texture reloads. See https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md for instructions on how to upgrade your custom backend. }; // Enumeration for PushStyleColor() / PopStyleColor() @@ -1751,10 +1781,12 @@ enum ImGuiCol_ ImGuiCol_TextLink, // Hyperlink color ImGuiCol_TextSelectedBg, // Selected text inside an InputText ImGuiCol_TreeLines, // Tree node hierarchy outlines when using ImGuiTreeNodeFlags_DrawLines - ImGuiCol_DragDropTarget, // Rectangle highlighting a drop target + ImGuiCol_DragDropTarget, // Rectangle border highlighting a drop target + ImGuiCol_DragDropTargetBg, // Rectangle background highlighting a drop target + ImGuiCol_UnsavedMarker, // Unsaved Document marker (in window title and tabs) ImGuiCol_NavCursor, // Color of keyboard/gamepad navigation cursor/rectangle, when visible - ImGuiCol_NavWindowingHighlight, // Highlight window when using CTRL+TAB - ImGuiCol_NavWindowingDimBg, // Darken/colorize entire screen behind the CTRL+TAB window list, when active + ImGuiCol_NavWindowingHighlight, // Highlight window when using Ctrl+Tab + ImGuiCol_NavWindowingDimBg, // Darken/colorize entire screen behind the Ctrl+Tab window list, when active ImGuiCol_ModalWindowDimBg, // Darken/colorize entire screen behind a modal window, when one is active ImGuiCol_COUNT, @@ -1770,9 +1802,9 @@ enum ImGuiCol_ // - The enum only refers to fields of ImGuiStyle which makes sense to be pushed/popped inside UI code. // During initialization or between frames, feel free to just poke into ImGuiStyle directly. // - Tip: Use your programming IDE navigation facilities on the names in the _second column_ below to find the actual members and their description. -// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. -// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments. -// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments. +// - In Visual Studio: Ctrl+Comma ("Edit.GoToAll") can follow symbols inside comments, whereas Ctrl+F12 ("Edit.GoToImplementation") cannot. +// - In Visual Studio w/ Visual Assist installed: Alt+G ("VAssistX.GoToImplementation") can also follow symbols inside comments. +// - In VS Code, CLion, etc.: Ctrl+Click can follow symbols inside comments. // - When changing this enum, you need to update the associated internal table GStyleVarInfo[] accordingly. This is where we link enum values to members offset/type. enum ImGuiStyleVar_ { @@ -1797,11 +1829,14 @@ enum ImGuiStyleVar_ ImGuiStyleVar_CellPadding, // ImVec2 CellPadding ImGuiStyleVar_ScrollbarSize, // float ScrollbarSize ImGuiStyleVar_ScrollbarRounding, // float ScrollbarRounding + ImGuiStyleVar_ScrollbarPadding, // float ScrollbarPadding ImGuiStyleVar_GrabMinSize, // float GrabMinSize ImGuiStyleVar_GrabRounding, // float GrabRounding ImGuiStyleVar_ImageBorderSize, // float ImageBorderSize ImGuiStyleVar_TabRounding, // float TabRounding ImGuiStyleVar_TabBorderSize, // float TabBorderSize + ImGuiStyleVar_TabMinWidthBase, // float TabMinWidthBase + ImGuiStyleVar_TabMinWidthShrink, // float TabMinWidthShrink ImGuiStyleVar_TabBarBorderSize, // float TabBarBorderSize ImGuiStyleVar_TabBarOverlineSize, // float TabBarOverlineSize ImGuiStyleVar_TableAngledHeadersAngle, // float TableAngledHeadersAngle @@ -1843,17 +1878,18 @@ enum ImGuiColorEditFlags_ ImGuiColorEditFlags_NoSidePreview = 1 << 8, // // ColorPicker: disable bigger color preview on right side of the picker, use small color square preview instead. ImGuiColorEditFlags_NoDragDrop = 1 << 9, // // ColorEdit: disable drag and drop target. ColorButton: disable drag and drop source. ImGuiColorEditFlags_NoBorder = 1 << 10, // // ColorButton: disable border (which is enforced by default) + ImGuiColorEditFlags_NoColorMarkers = 1 << 11, // // ColorEdit: disable rendering R/G/B/A color marker. May also be disabled globally by setting style.ColorMarkerSize = 0. // Alpha preview // - Prior to 1.91.8 (2025/01/21): alpha was made opaque in the preview by default using old name ImGuiColorEditFlags_AlphaPreview. // - We now display the preview as transparent by default. You can use ImGuiColorEditFlags_AlphaOpaque to use old behavior. // - The new flags may be combined better and allow finer controls. - ImGuiColorEditFlags_AlphaOpaque = 1 << 11, // // ColorEdit, ColorPicker, ColorButton: disable alpha in the preview,. Contrary to _NoAlpha it may still be edited when calling ColorEdit4()/ColorPicker4(). For ColorButton() this does the same as _NoAlpha. - ImGuiColorEditFlags_AlphaNoBg = 1 << 12, // // ColorEdit, ColorPicker, ColorButton: disable rendering a checkerboard background behind transparent color. - ImGuiColorEditFlags_AlphaPreviewHalf= 1 << 13, // // ColorEdit, ColorPicker, ColorButton: display half opaque / half transparent preview. + ImGuiColorEditFlags_AlphaOpaque = 1 << 12, // // ColorEdit, ColorPicker, ColorButton: disable alpha in the preview,. Contrary to _NoAlpha it may still be edited when calling ColorEdit4()/ColorPicker4(). For ColorButton() this does the same as _NoAlpha. + 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). - ImGuiColorEditFlags_AlphaBar = 1 << 16, // // ColorEdit, ColorPicker: show vertical alpha bar/gradient in picker. + 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. ImGuiColorEditFlags_DisplayHSV = 1 << 21, // [Display] // " @@ -1878,7 +1914,7 @@ 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. + 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] }; @@ -1891,13 +1927,14 @@ enum ImGuiSliderFlags_ ImGuiSliderFlags_None = 0, ImGuiSliderFlags_Logarithmic = 1 << 5, // Make the widget logarithmic (linear otherwise). Consider using ImGuiSliderFlags_NoRoundToFormat with this if using a format-string with small amount of digits. ImGuiSliderFlags_NoRoundToFormat = 1 << 6, // Disable rounding underlying value to match precision of the display format string (e.g. %.3f values are rounded to those 3 digits). - ImGuiSliderFlags_NoInput = 1 << 7, // Disable CTRL+Click or Enter key allowing to input text directly into the widget. + ImGuiSliderFlags_NoInput = 1 << 7, // Disable Ctrl+Click or Enter key allowing to input text directly into the widget. ImGuiSliderFlags_WrapAround = 1 << 8, // Enable wrapping around from max to min and from min to max. Only supported by DragXXX() functions for now. - ImGuiSliderFlags_ClampOnInput = 1 << 9, // Clamp value to min/max bounds when input manually with CTRL+Click. By default CTRL+Click allows going out of bounds. + ImGuiSliderFlags_ClampOnInput = 1 << 9, // Clamp value to min/max bounds when input manually with Ctrl+Click. By default Ctrl+Click allows going out of bounds. ImGuiSliderFlags_ClampZeroRange = 1 << 10, // Clamp even if min==max==0.0f. Otherwise due to legacy reason DragXXX functions don't clamp with those values. When your clamping limits are dynamic you almost always want to use it. 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_InvalidMask_ = 0x7000000F, // [Internal] We treat using those bits as being potentially a 'float power' argument from the previous API that has got miscast to this enum, and will trigger an assert if needed. + 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. }; // Identify a mouse button. @@ -1987,7 +2024,7 @@ enum ImGuiTableFlags_ ImGuiTableFlags_Reorderable = 1 << 1, // Enable reordering columns in header row (need calling TableSetupColumn() + TableHeadersRow() to display headers) ImGuiTableFlags_Hideable = 1 << 2, // Enable hiding/disabling columns in context menu. ImGuiTableFlags_Sortable = 1 << 3, // Enable sorting. Call TableGetSortSpecs() to obtain sort specs. Also see ImGuiTableFlags_SortMulti and ImGuiTableFlags_SortTristate. - ImGuiTableFlags_NoSavedSettings = 1 << 4, // Disable persisting columns order, width and sort settings in the .ini file. + ImGuiTableFlags_NoSavedSettings = 1 << 4, // Disable persisting columns order, width, visibility and sort settings in the .ini file. ImGuiTableFlags_ContextMenuInBody = 1 << 5, // Right-click on columns body/contents will display table context menu. By default it is available in TableHeadersRow(). // Decorations ImGuiTableFlags_RowBg = 1 << 6, // Set each RowBg color with ImGuiCol_TableRowBg or ImGuiCol_TableRowBgAlt (equivalent of calling TableSetBgColor with ImGuiTableBgFlags_RowBg0 on each row manually) @@ -2173,7 +2210,7 @@ struct ImVector // Constructors, destructor inline ImVector() { Size = Capacity = 0; Data = NULL; } inline ImVector(const ImVector& src) { Size = Capacity = 0; Data = NULL; operator=(src); } - inline ImVector& operator=(const ImVector& src) { clear(); resize(src.Size); if (src.Data) memcpy(Data, src.Data, (size_t)Size * sizeof(T)); return *this; } + inline ImVector& operator=(const ImVector& src) { clear(); resize(src.Size); if (Data && src.Data) memcpy(Data, src.Data, (size_t)Size * sizeof(T)); return *this; } inline ~ImVector() { if (Data) IM_FREE(Data); } // Important: does not destruct anything inline void clear() { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } } // Important: does not destruct anything @@ -2263,12 +2300,15 @@ struct ImGuiStyle float ColumnsMinSpacing; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1). float ScrollbarSize; // Width of the vertical scrollbar, Height of the horizontal scrollbar. float ScrollbarRounding; // Radius of grab corners for scrollbar. + float ScrollbarPadding; // Padding of scrollbar grab within its frame (same for both axes). float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar. float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. float LogSliderDeadzone; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero. float ImageBorderSize; // Thickness of border around Image() calls. float TabRounding; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. float TabBorderSize; // Thickness of border around tabs. + float TabMinWidthBase; // Minimum tab width, to make tabs larger than their contents. TabBar buttons are not affected. + float TabMinWidthShrink; // Minimum tab width after shrinking, when using ImGuiTabBarFlags_FittingPolicyMixed policy. float TabCloseButtonMinWidthSelected; // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width. float TabCloseButtonMinWidthUnselected; // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width. FLT_MAX: never show close button when unselected. float TabBarBorderSize; // Thickness of tab-bar separator, which takes on the tab active color to denote focus. @@ -2278,6 +2318,10 @@ 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 DragDropTargetRounding; // Radius of the drag and drop target frame. + 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. + float ColorMarkerSize; // Size of R/G/B/A color markers for ColorEdit4() and for Drags/Sliders when using ImGuiSliderFlags_ColorMarkers. 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. @@ -2361,7 +2405,7 @@ struct ImGuiIO // Font system ImFontAtlas*Fonts; // // Font atlas: load, rasterize and pack one or more fonts into a single texture. ImFont* FontDefault; // = NULL // Font to use on NewFrame(). Use NULL to uses Fonts->Fonts[0]. - bool FontAllowUserScaling; // = false // [OBSOLETE] Allow user scaling text of individual window with CTRL+Wheel. + bool FontAllowUserScaling; // = false // Allow user scaling text of individual window with Ctrl+Wheel. // Keyboard/Gamepad Navigation options bool ConfigNavSwapGamepadButtons; // = false // Swap Activate<>Cancel (A<>B) buttons, matching typical "Nintendo/Japanese style" gamepad layout. @@ -2382,7 +2426,7 @@ struct ImGuiIO 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 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. float ConfigMemoryCompactTimer; // = 60.0f // Timer (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable. @@ -2420,7 +2464,7 @@ struct ImGuiIO // Option to enable various debug tools showing buttons that will call the IM_DEBUG_BREAK() macro. // - The Item Picker tool will be available regardless of this being enabled, in order to maximize its discoverability. // - Requires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application. - // e.g. io.ConfigDebugIsDebuggerPresent = ::IsDebuggerPresent() on Win32, or refer to ImOsIsDebuggerPresent() imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version). + // e.g. io.ConfigDebugIsDebuggerPresent = ::IsDebuggerPresent() on Win32, or refer to ImOsIsDebuggerPresent() imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version. bool ConfigDebugIsDebuggerPresent; // = false // Enable various tools calling IM_DEBUG_BREAK(). // Tools to detect code submitting items with conflicting/duplicate IDs @@ -2479,9 +2523,6 @@ struct ImGuiIO IMGUI_API void ClearEventsQueue(); // Clear all incoming events. IMGUI_API void ClearInputKeys(); // Clear current keyboard/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons. IMGUI_API void ClearInputMouse(); // Clear current mouse state. -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - IMGUI_API void ClearInputCharacters(); // [Obsoleted in 1.89.8] Clear the current frame text input buffer. Now included within ClearInputKeys(). -#endif //------------------------------------------------------------------ // Output - Updated by NewFrame() or EndFrame()/Render() @@ -2514,16 +2555,16 @@ struct ImGuiIO // (reading from those variables is fair game, as they are extremely unlikely to be moving anywhere) ImVec2 MousePos; // Mouse position, in pixels. Set to ImVec2(-FLT_MAX, -FLT_MAX) if mouse is unavailable (on another screen, etc.) bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras (ImGuiMouseButton_COUNT == 5). Dear ImGui mostly uses left and right buttons. Other buttons allow us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. - float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. >0 scrolls Up, <0 scrolls Down. Hold SHIFT to turn vertical scroll into horizontal scroll. + float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. >0 scrolls Up, <0 scrolls Down. Hold Shift to turn vertical scroll into horizontal scroll. float MouseWheelH; // Mouse wheel Horizontal. >0 scrolls Left, <0 scrolls Right. Most users don't have a mouse with a horizontal wheel, may not be filled by all backends. ImGuiMouseSource MouseSource; // Mouse actual input peripheral (Mouse/TouchScreen/Pen). - bool KeyCtrl; // Keyboard modifier down: Control + bool KeyCtrl; // Keyboard modifier down: Ctrl (non-macOS), Cmd (macOS) bool KeyShift; // Keyboard modifier down: Shift bool KeyAlt; // Keyboard modifier down: Alt - bool KeySuper; // Keyboard modifier down: Cmd/Super/Windows + bool KeySuper; // Keyboard modifier down: Windows/Super (non-macOS), Ctrl (macOS) // Other state maintained from data above + IO function calls - ImGuiKeyChord KeyMods; // Key mods flags (any of ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Alt/ImGuiMod_Super flags, same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags. Read-only, updated by NewFrame() + ImGuiKeyChord KeyMods; // Key mods flags (any of ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Alt/ImGuiMod_Super flags, same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags). Read-only, updated by NewFrame() ImGuiKeyData KeysData[ImGuiKey_NamedKey_COUNT];// Key state for all known keys. MUST use 'key - ImGuiKey_NamedKey_BEGIN' as index. Use IsKeyXXX() functions to access this. bool WantCaptureMouseUnlessPopupClose; // Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup. ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid) @@ -2537,8 +2578,8 @@ struct ImGuiIO double MouseReleasedTime[5]; // Time of last released (rarely used! but useful to handle delayed single-click when trying to disambiguate them from double-click). bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window or over void blocked by a popup. We don't request mouse capture from the application if click started outside ImGui bounds. bool MouseDownOwnedUnlessPopupClose[5]; // Track if button was clicked inside a dear imgui window. - bool MouseWheelRequestAxisSwap; // On a non-Mac system, holding SHIFT requests WheelY to perform the equivalent of a WheelX event. On a Mac system this is already enforced by the system. - bool MouseCtrlLeftAsRightClick; // (OSX) Set to true when the current click was a Ctrl+click that spawned a simulated right click + bool MouseWheelRequestAxisSwap; // On a non-Mac system, holding Shift requests WheelY to perform the equivalent of a WheelX event. On a Mac system this is already enforced by the system. + bool MouseCtrlLeftAsRightClick; // (OSX) Set to true when the current click was a Ctrl+Click that spawned a simulated right click float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked) float MouseDownDurationPrev[5]; // Previous time the mouse button has been down float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the clicking point (used for moving thresholds) @@ -2566,6 +2607,8 @@ struct ImGuiIO const char* (*GetClipboardTextFn)(void* user_data); void (*SetClipboardTextFn)(void* user_data, const char* text); void* ClipboardUserData; + + //void ClearInputCharacters() { InputQueueCharacters.resize(0); } // [Obsoleted in 1.89.8] Clear the current frame text input buffer. Now included within ClearInputKeys(). Removed this as it is ambiguous/misleading and generally incorrect to use with the existence of a higher-level input queue. #endif IMGUI_API ImGuiIO(); @@ -2600,10 +2643,10 @@ struct ImGuiInputTextCallbackData ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only // [Completion,History] char* Buf; // Text buffer // Read-write // [Resize] Can replace pointer / [Completion,History,Always] Only write to pointed data, don't replace the actual pointer! int BufTextLen; // Text length (in bytes) // Read-write // [Resize,Completion,History,Always] Exclude zero-terminator storage. In C land: == strlen(some_text), in C++ land: string.length() - int BufSize; // Buffer size (in bytes) = capacity+1 // Read-only // [Resize,Completion,History,Always] Include zero-terminator storage. In C land == ARRAYSIZE(my_char_array), in C++ land: string.capacity()+1 + int BufSize; // Buffer size (in bytes) = capacity+1 // Read-only // [Resize,Completion,History,Always] Include zero-terminator storage. In C land: == ARRAYSIZE(my_char_array), in C++ land: string.capacity()+1 bool BufDirty; // Set if you modify Buf/BufTextLen! // Write // [Completion,History,Always] int CursorPos; // // Read-write // [Completion,History,Always] - int SelectionStart; // // Read-write // [Completion,History,Always] == to SelectionEnd when no selection) + int SelectionStart; // // Read-write // [Completion,History,Always] == to SelectionEnd when no selection int SelectionEnd; // // Read-write // [Completion,History,Always] // Helper functions for text manipulation. @@ -2772,6 +2815,13 @@ struct ImGuiStorage #endif }; +// Flags for ImGuiListClipper (currently not fully exposed in function calls: a future refactor will likely add this to ImGuiListClipper::Begin function equivalent) +enum ImGuiListClipperFlags_ +{ + ImGuiListClipperFlags_None = 0, + ImGuiListClipperFlags_NoSetTableRowCounters = 1 << 0, // [Internal] Disabled modifying table row counters. Avoid assumption that 1 clipper item == 1 table row. +}; + // Helper: Manually clip large list of items. // If you have lots evenly spaced items and you have random access to the list, you can perform coarse // clipping based on visibility to only submit items that are in view. @@ -2802,6 +2852,7 @@ struct ImGuiListClipper double StartPosY; // [Internal] Cursor position at the time of Begin() or after table frozen rows are all processed double StartSeekOffsetY; // [Internal] Account for frozen rows in a table and initial loss of precision in very large windows. void* TempData; // [Internal] Internal data + ImGuiListClipperFlags Flags; // [Internal] Flags, currently not yet well exposed. // items_count: Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step, and you can call SeekCursorForItem() manually if you need) // items_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetFrameHeightWithSpacing(). @@ -2822,7 +2873,7 @@ struct ImGuiListClipper IMGUI_API void SeekCursorForItem(int item_index); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - inline void IncludeRangeByIndices(int item_begin, int item_end) { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.9] + //inline void IncludeRangeByIndices(int item_begin, int item_end) { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.9] //inline void ForceDisplayRangeByIndices(int item_begin, int item_end) { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.6] //inline ImGuiListClipper(int items_count, float items_height = -1.0f) { memset(this, 0, sizeof(*this)); ItemsCount = -1; Begin(items_count, items_height); } // [removed in 1.79] #endif @@ -2916,7 +2967,7 @@ struct ImColor // Multi-selection system // Documentation at: https://github.com/ocornut/imgui/wiki/Multi-Select // - Refer to 'Demo->Widgets->Selection State & Multi-Select' for demos using this. -// - This system implements standard multi-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc) +// - This system implements standard multi-selection idioms (Ctrl+Mouse/Keyboard, Shift+Mouse/Keyboard, etc) // with support for clipper (skipping non-visible items), box-select and many other details. // - Selectable(), Checkbox() are supported but custom widgets may use it as well. // - TreeNode() is technically supported but... using this correctly is more complicated: you need some sort of linear/random access to your tree, @@ -2954,7 +3005,7 @@ enum ImGuiMultiSelectFlags_ { ImGuiMultiSelectFlags_None = 0, ImGuiMultiSelectFlags_SingleSelect = 1 << 0, // Disable selecting more than one item. This is available to allow single-selection code to share same code/logic if desired. It essentially disables the main purpose of BeginMultiSelect() tho! - ImGuiMultiSelectFlags_NoSelectAll = 1 << 1, // Disable CTRL+A shortcut to select all. + ImGuiMultiSelectFlags_NoSelectAll = 1 << 1, // Disable Ctrl+A shortcut to select all. ImGuiMultiSelectFlags_NoRangeSelect = 1 << 2, // Disable Shift+selection mouse/keyboard support (useful for unordered 2D selection). With BoxSelect is also ensure contiguous SetRange requests are not combined into one. This allows not handling interpolation in SetRange requests. ImGuiMultiSelectFlags_NoAutoSelect = 1 << 3, // Disable selecting items when navigating (useful for e.g. supporting range-select in a list of checkboxes). ImGuiMultiSelectFlags_NoAutoClear = 1 << 4, // Disable clearing selection when navigating or selecting another one (generally used with ImGuiMultiSelectFlags_NoAutoSelect. useful for e.g. supporting range-select in a list of checkboxes). @@ -2970,6 +3021,7 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 14, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. //ImGuiMultiSelectFlags_RangeSelect2d = 1 << 15, // Shift+Selection uses 2d geometry instead of linear sequence, so possible to use Shift+up/down to select vertically in grid. Analogous to what BoxSelect does. 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. }; // Main IO structure returned by BeginMultiSelect()/EndMultiSelect(). @@ -3112,7 +3164,7 @@ struct ImDrawCmd // Since 1.83: returns ImTextureID associated with this draw call. Warning: DO NOT assume this is always same as 'TextureId' (we will change this function for an upcoming feature) // Since 1.92: removed ImDrawCmd::TextureId field, the getter function must be used! - inline ImTextureID GetTexID() const; // == (TexRef._TexData ? TexRef._TexData->TexID : TexRef._TexID + inline ImTextureID GetTexID() const; // == (TexRef._TexData ? TexRef._TexData->TexID : TexRef._TexID) }; // Vertex layout @@ -3308,7 +3360,7 @@ struct ImDrawList // Advanced: Miscellaneous IMGUI_API void AddDrawCmd(); // This is useful if you need to forcefully create a new draw call (to allow for dependent rendering / blending). Otherwise primitives are merged into the same draw-call as much as possible - IMGUI_API ImDrawList* CloneOutput() const; // Create a clone of the CmdBuffer/IdxBuffer/VtxBuffer. + IMGUI_API ImDrawList* CloneOutput() const; // Create a clone of the CmdBuffer/IdxBuffer/VtxBuffer. For multi-threaded rendering, consider using `imgui_threaded_rendering` from https://github.com/ocornut/imgui_club instead. // Advanced: Channels // - Use to split render into layers. By switching channels to can render out-of-order (e.g. submit FG primitives before BG primitives) @@ -3334,8 +3386,8 @@ struct ImDrawList // Obsolete names #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - inline void PushTextureID(ImTextureRef tex_ref) { PushTexture(tex_ref); } // RENAMED in 1.92.x - inline void PopTextureID() { PopTexture(); } // RENAMED in 1.92.x + inline void PushTextureID(ImTextureRef tex_ref) { PushTexture(tex_ref); } // RENAMED in 1.92.0 + inline void PopTextureID() { PopTexture(); } // RENAMED in 1.92.0 #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) //inline void AddEllipseFilled(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0) { AddEllipseFilled(center, ImVec2(radius_x, radius_y), col, rot, num_segments); } // OBSOLETED in 1.90.5 (Mar 2024) @@ -3364,7 +3416,7 @@ struct ImDrawList struct ImDrawData { bool Valid; // Only valid after Render() is called and before the next NewFrame() is called. - int CmdListsCount; // Number of ImDrawList* to render. (== CmdLists.Size). Exists for legacy reason. + int CmdListsCount; // == CmdLists.Size. (OBSOLETE: exists for legacy reasons). Number of ImDrawList* to render. 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. @@ -3372,7 +3424,7 @@ struct ImDrawData ImVec2 DisplaySize; // Size of the viewport to render (== GetMainViewport()->Size for the main viewport, == io.DisplaySize in most single-viewport applications) ImVec2 FramebufferScale; // Amount of pixels for each unit of DisplaySize. Copied from viewport->FramebufferScale (== io.DisplayFramebufferScale for main viewport). Generally (1,1) on normal display, (2,2) on OSX with Retina display. 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 overriden or set to NULL if you want to manually update textures. + 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. // Functions ImDrawData() { Clear(); } @@ -3390,6 +3442,8 @@ struct ImDrawData // FOR ALL OTHER ImTextureXXXX TYPES: ONLY CORE LIBRARY AND RENDERER BACKENDS NEED TO KNOW AND CARE ABOUT THEM. //----------------------------------------------------------------------------- +#undef Status // X11 headers are leaking this. + // We intentionally support a limited amount of texture formats to limit burden on CPU-side code and extension. // Most standard backends only support RGBA32 but we provide a single channel option for low-resource/embedded systems. enum ImTextureFormat @@ -3427,7 +3481,7 @@ struct ImTextureRect struct ImTextureData { //------------------------------------------ core / backend --------------------------------------- - int UniqueID; // w - // Sequential index to facilitate identifying a texture when debugging/printing. Unique per atlas. + 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. 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. @@ -3445,7 +3499,7 @@ struct ImTextureData bool WantDestroyNextFrame; // rw - // [Internal] Queued to set ImTextureStatus_WantDestroy next frame. May still be used in the current frame. // Functions - ImTextureData() { memset(this, 0, sizeof(*this)); } + ImTextureData() { memset(this, 0, sizeof(*this)); Status = ImTextureStatus_Destroyed; TexID = ImTextureID_Invalid; } ~ImTextureData() { DestroyPixels(); } IMGUI_API void Create(ImTextureFormat format, int w, int h); IMGUI_API void DestroyPixels(); @@ -3457,8 +3511,10 @@ struct ImTextureData ImTextureID GetTexID() const { return TexID; } // Called by Renderer backend - void SetTexID(ImTextureID tex_id) { TexID = tex_id; } // Call after creating or destroying the texture. Never modify TexID directly! - void SetStatus(ImTextureStatus status) { Status = status; } // Call after honoring a request. Never modify Status directly! + // - Call SetTexID() and SetStatus() after honoring texture requests. Never modify TexID and Status directly! + // - A backend may decide to destroy a texture that we did not request to destroy, which is fine (e.g. freeing resources), but we immediately set the texture back in _WantCreate mode. + void SetTexID(ImTextureID tex_id) { TexID = tex_id; } + void SetStatus(ImTextureStatus status) { Status = status; if (status == ImTextureStatus_Destroyed && !WantDestroyNextFrame && Pixels != nullptr) Status = ImTextureStatus_WantCreate; } }; //----------------------------------------------------------------------------- @@ -3472,16 +3528,15 @@ struct ImFontConfig char Name[40]; // // Name (strictly to ease debugging, hence limited size buffer) void* FontData; // // TTF/OTF data int FontDataSize; // // TTF/OTF data size - bool FontDataOwnedByAtlas; // true // TTF/OTF data ownership taken by the container ImFontAtlas (will delete memory itself). + bool FontDataOwnedByAtlas; // true // TTF/OTF data ownership taken by the owner ImFontAtlas (will delete memory itself). SINCE 1.92, THE DATA NEEDS TO PERSIST FOR WHOLE DURATION OF ATLAS. // Options bool MergeMode; // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights. - bool PixelSnapH; // false // Align every glyph AdvanceX to pixel boundaries. Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1. - bool PixelSnapV; // true // Align Scaled GlyphOffset.y to pixel boundaries. - ImS8 FontNo; // 0 // Index of font within TTF/OTF file + bool PixelSnapH; // false // Align every glyph AdvanceX to pixel boundaries. Prevents fractional font size from working correctly! Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1. ImS8 OversampleH; // 0 (2) // Rasterize at higher quality for sub-pixel positioning. 0 == auto == 1 or 2 depending on size. Note the difference between 2 and 3 is minimal. You can reduce this to 1 for large glyphs save memory. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details. ImS8 OversampleV; // 0 (1) // Rasterize at higher quality for sub-pixel positioning. 0 == auto == 1. This is not really useful as we don't use sub-pixel positions on the Y axis. - float SizePixels; // // Size in pixels for rasterizer (more or less maps to the resulting font height). + ImWchar EllipsisChar; // 0 // Explicitly specify Unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used. + float SizePixels; // // Output size in pixels for rasterizer (more or less maps to the resulting font height). float Weight; const ImWchar* GlyphRanges; // NULL // *LEGACY* THE ARRAY DATA NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE. Pointer to a user-provided list of Unicode range (2 value per range, values are inclusive, zero-terminated list). const ImWchar* GlyphExcludeRanges; // NULL // Pointer to a small user-provided list of Unicode ranges (2 value per range, values are inclusive, zero-terminated list). This is very close to GlyphRanges[] but designed to exclude ranges from a font source, when merging fonts with overlapping glyphs. Use "Input Glyphs Overlap Detection Tool" to find about your overlapping ranges. @@ -3490,18 +3545,22 @@ struct ImFontConfig float GlyphMinAdvanceX; // 0 // Minimum AdvanceX for glyphs, set Min to align font icons, set both Min/Max to enforce mono-space font. Absolute value for default size, other sizes will scale this value. float GlyphMaxAdvanceX; // FLT_MAX // Maximum AdvanceX for glyphs float GlyphExtraAdvanceX; // 0 // Extra spacing (in pixels) between glyphs. Please contact us if you are using this. // FIXME-NEWATLAS: Intentionally unscaled + ImU32 FontNo; // 0 // Index of font within TTF/OTF file unsigned int FontLoaderFlags; // 0 // Settings for custom font builder. THIS IS BUILDER IMPLEMENTATION DEPENDENT. Leave as zero if unsure. - //unsigned int FontBuilderFlags; // -- // [Renamed in 1.92] Ue FontLoaderFlags. + //unsigned int FontBuilderFlags; // -- // [Renamed in 1.92] Use FontLoaderFlags. float RasterizerMultiply; // 1.0f // Linearly brighten (>1.0f) or darken (<1.0f) font output. Brightening small fonts may be a good workaround to make them more readable. This is a silly thing we may remove in the future. float RasterizerDensity; // 1.0f // [LEGACY: this only makes sense when ImGuiBackendFlags_RendererHasTextures is not supported] DPI scale multiplier for rasterization. Not altering other font metrics: makes it easy to swap between e.g. a 100% and a 400% fonts for a zooming display, or handle Retina screen. IMPORTANT: If you change this it is expected that you increase/decrease font scale roughly to the inverse of this, otherwise quality may look lowered. - ImWchar EllipsisChar; // 0 // Explicitly specify Unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used. + float ExtraSizeScale; // 1.0f // Extra rasterizer scale over SizePixels. // [Internal] ImFontFlags Flags; // Font flags (don't use just yet, will be exposed in upcoming 1.92.X updates) ImFont* DstFont; // Target font (as we merging fonts, multiple ImFontConfig may target the same font) - const ImFontLoader* FontLoader; // Custom font backend for this source (other use one stored in ImFontAtlas) + const ImFontLoader* FontLoader; // Custom font backend for this source (default source is the one stored in ImFontAtlas) void* FontLoaderData; // Font loader opaque storage (per font config) +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + bool PixelSnapV; // true // [Obsoleted in 1.91.6] Align Scaled GlyphOffset.y to pixel boundaries. +#endif IMGUI_API ImFontConfig(); }; @@ -3587,15 +3646,18 @@ struct ImFontAtlas IMGUI_API ImFontAtlas(); IMGUI_API ~ImFontAtlas(); IMGUI_API ImFont* AddFont(const ImFontConfig* font_cfg); - IMGUI_API ImFont* AddFontDefault(const ImFontConfig* font_cfg = NULL); + IMGUI_API ImFont* AddFontDefault(const ImFontConfig* font_cfg = NULL); // Selects between AddFontDefaultVector() and AddFontDefaultBitmap(). + IMGUI_API ImFont* AddFontDefaultVector(const ImFontConfig* font_cfg = NULL); // Embedded scalable font. Recommended at any higher size. + IMGUI_API ImFont* AddFontDefaultBitmap(const ImFontConfig* font_cfg = NULL); // Embedded classic pixel-clean font. Recommended at Size 13px with no scaling. IMGUI_API ImFont* AddFontFromFileTTF(const char* filename, float size_pixels = 0.0f, float weight = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); IMGUI_API ImFont* AddFontFromMemoryTTF(void* font_data, int font_data_size, float size_pixels = 0.0f, float weight = 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, float weight = 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, float weight = 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 (input fonts, output glyphs/textures) + IMGUI_API void Clear(); // Clear everything (input fonts, output glyphs/textures). 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: 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. @@ -3607,7 +3669,7 @@ struct ImFontAtlas // - User is in charge of copying the pixels into graphics memory (e.g. create a texture with your engine). Then store your texture handle with SetTexID(). // - The pitch is always = Width * BytesPerPixels (1 or 4) // - Building in RGBA32 format is provided for convenience and compatibility, but note that unless you manually manipulate or copy color data into - // the texture (e.g. when using the AddCustomRect*** api), then the RGB pixels emitted will always be white (~75% of memory/bandwidth waste. + // the texture (e.g. when using the AddCustomRect*** api), then the RGB pixels emitted will always be white (~75% of memory/bandwidth waste). // - From 1.92 with backends supporting ImGuiBackendFlags_RendererHasTextures: // - Calling Build(), GetTexDataAsAlpha8(), GetTexDataAsRGBA32() is not needed. // - In backend: replace calls to ImFontAtlas::SetTexID() with calls to ImTextureData::SetTexID() after honoring texture creation. @@ -3646,7 +3708,7 @@ struct ImFontAtlas // Register and retrieve custom rectangles // - You can request arbitrary rectangles to be packed into the atlas, for your own purpose. - // - Since 1.92.X, packing is done immediately in the function call (previously packing was done during the Build call) + // - Since 1.92.0, packing is done immediately in the function call (previously packing was done during the Build call) // - You can render your pixels into the texture right after calling the AddCustomRect() functions. // - VERY IMPORTANT: // - Texture may be created/resized at any time when calling ImGui or ImFontAtlas functions. @@ -3685,7 +3747,7 @@ struct ImFontAtlas #ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImTextureRef TexRef; // Latest texture identifier == TexData->GetTexRef(). #else - union { ImTextureRef TexRef; ImTextureRef TexID; }; // Latest texture identifier == TexData->GetTexRef(). // RENAMED TexID to TexRef in 1.92.x + union { ImTextureRef TexRef; ImTextureRef TexID; }; // Latest texture identifier == TexData->GetTexRef(). // RENAMED TexID to TexRef in 1.92.0. #endif ImTextureData* TexData; // Latest texture. @@ -3704,7 +3766,7 @@ struct ImFontAtlas int FontNextUniqueID; // Next value to be stored in ImFont->FontID ImVector DrawListSharedDatas; // List of users for this atlas. Typically one per Dear ImGui context. ImFontAtlasBuilder* Builder; // Opaque interface to our data that doesn't need to be public and may be discarded when rebuilding. - const ImFontLoader* FontLoader; // Font loader opaque interface (default to stb_truetype, can be changed to use FreeType by defining IMGUI_ENABLE_FREETYPE). Don't set directly! + const ImFontLoader* FontLoader; // Font loader opaque interface (default to use FreeType when IMGUI_ENABLE_FREETYPE is defined, otherwise default to use stb_truetype). Use SetFontLoader() to change this at runtime. const char* FontLoaderName; // Font loader name (for display e.g. in About box) == FontLoader->Name void* FontLoaderData; // Font backend opaque storage unsigned int FontLoaderFlags; // Shared flags (for all fonts) for font loader. THIS IS BUILD IMPLEMENTATION DEPENDENT (e.g. Per-font override is also available in ImFontConfig). @@ -3715,15 +3777,15 @@ struct ImFontAtlas #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // Legacy: You can request your rectangles to be mapped as font glyph (given a font + Unicode point), so you can render e.g. custom colorful icons and use them as regular glyphs. --> Prefer using a custom ImFontLoader. ImFontAtlasRect TempRect; // For old GetCustomRectByIndex() API - inline ImFontAtlasRectId AddCustomRectRegular(int w, int h) { return AddCustomRect(w, h); } // RENAMED in 1.92.X - inline const ImFontAtlasRect* GetCustomRectByIndex(ImFontAtlasRectId id) { return GetCustomRect(id, &TempRect) ? &TempRect : NULL; } // OBSOLETED in 1.92.X - inline void CalcCustomRectUV(const ImFontAtlasRect* r, ImVec2* out_uv_min, ImVec2* out_uv_max) const { *out_uv_min = r->uv0; *out_uv_max = r->uv1; } // OBSOLETED in 1.92.X - IMGUI_API ImFontAtlasRectId AddCustomRectFontGlyph(ImFont* font, ImWchar codepoint, int w, int h, float advance_x, const ImVec2& offset = ImVec2(0, 0)); // OBSOLETED in 1.92.X: Use custom ImFontLoader in ImFontConfig - IMGUI_API ImFontAtlasRectId AddCustomRectFontGlyphForSize(ImFont* font, float font_size, float font_weight, ImWchar codepoint, int w, int h, float advance_x, const ImVec2& offset = ImVec2(0, 0)); // ADDED AND OBSOLETED in 1.92.X + inline ImFontAtlasRectId AddCustomRectRegular(int w, int h) { return AddCustomRect(w, h); } // RENAMED in 1.92.0 + inline const ImFontAtlasRect* GetCustomRectByIndex(ImFontAtlasRectId id) { return GetCustomRect(id, &TempRect) ? &TempRect : NULL; } // OBSOLETED in 1.92.0 + inline void CalcCustomRectUV(const ImFontAtlasRect* r, ImVec2* out_uv_min, ImVec2* out_uv_max) const { *out_uv_min = r->uv0; *out_uv_max = r->uv1; } // OBSOLETED in 1.92.0 + IMGUI_API ImFontAtlasRectId AddCustomRectFontGlyph(ImFont* font, ImWchar codepoint, int w, int h, float advance_x, const ImVec2& offset = ImVec2(0, 0)); // OBSOLETED in 1.92.0: Use custom ImFontLoader in ImFontConfig + IMGUI_API ImFontAtlasRectId AddCustomRectFontGlyphForSize(ImFont* font, float font_size, float font_weight, ImWchar codepoint, int w, int h, float advance_x, const ImVec2& offset = ImVec2(0, 0)); // ADDED AND OBSOLETED in 1.92.0 #endif - //unsigned int FontBuilderFlags; // OBSOLETED in 1.92.X: Renamed to FontLoaderFlags. - //int TexDesiredWidth; // OBSOLETED in 1.92.X: Force texture width before calling Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height) - //typedef ImFontAtlasRect ImFontAtlasCustomRect; // OBSOLETED in 1.92.X + //unsigned int FontBuilderFlags; // OBSOLETED in 1.92.0: Renamed to FontLoaderFlags. + //int TexDesiredWidth; // OBSOLETED in 1.92.0: Force texture width before calling Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height. + //typedef ImFontAtlasRect ImFontAtlasCustomRect; // OBSOLETED in 1.92.0 //typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+ //typedef ImFontGlyphRangesBuilder GlyphRangesBuilder; // OBSOLETED in 1.67+ }; @@ -3748,10 +3810,11 @@ struct ImFontBaked float Ascent, Descent; // 4+4 // out // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] (unscaled) unsigned int MetricsTotalSurface:26;// 3 // out // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs) unsigned int WantDestroy:1; // 0 // // Queued for destroy - unsigned int LockLoadingFallback:1; // 0 // // - int LastUsedFrame; // 4 // // Record of that time this was bounds - ImGuiID BakedId; // 4 // - ImFont* ContainerFont; // 4-8 // in // Parent font + unsigned int LoadNoFallback:1; // 0 // // Disable loading fallback in lower-level calls. + unsigned int LoadNoRenderOnLayout:1;// 0 // // Enable a two-steps mode where CalcTextSize() calls will load AdvanceX *without* rendering/packing glyphs. Only advantagous if you know that the glyph is unlikely to actually be rendered, otherwise it is slower because we'd do one query on the first CalcTextSize and one query on the first Draw. + int LastUsedFrame; // 4 // // Record of that time this was bounds + ImGuiID BakedId; // 4 // // Unique ID for this baked storage + ImFont* OwnerFont; // 4-8 // in // Parent font void* FontLoaderDatas; // 4-8 // // Font loader opaque storage (per baked font * sources): single contiguous buffer allocated by imgui, passed to loader. // Functions @@ -3775,14 +3838,14 @@ enum ImFontFlags_ // Font runtime data and rendering // - ImFontAtlas automatically loads a default embedded font for you if you didn't load one manually. -// - Since 1.92.X a font may be rendered as any size! Therefore a font doesn't have one specific size. +// - Since 1.92.0 a font may be rendered as any size! Therefore a font doesn't have one specific size. // - Use 'font->GetFontBaked(size)' to retrieve the ImFontBaked* corresponding to a given size. // - If you used g.Font + g.FontSize (which is frequent from the ImGui layer), you can use g.FontBaked as a shortcut, as g.FontBaked == g.Font->GetFontBaked(g.FontSize). struct ImFont { // [Internal] Members: Hot ~12-20 bytes ImFontBaked* LastBaked; // 4-8 // Cache last bound baked. NEVER USE DIRECTLY. Use GetFontBaked(). - ImFontAtlas* ContainerAtlas; // 4-8 // What we have been loaded into. + 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. @@ -3790,8 +3853,8 @@ struct ImFont // Conceptually Sources[] is the list of font sources merged to create this font. ImGuiID FontId; // Unique identifier for the font float LegacySize; // 4 // in // Font size passed to AddFont(). Use for old code calling PushFont() expecting to use that size. (use ImGui::GetFontBaked() to get font baked at current bound size). + ImVector Sources; // 16 // in // List of sources. Pointers within OwnerAtlas->Sources[] float DefaultWeight; // 4 // in // Default font weight - ImVector Sources; // 16 // in // List of sources. Pointers within ContainerAtlas->Sources[] ImWchar EllipsisChar; // 2-4 // out // Character used for ellipsis rendering ('...'). ImWchar FallbackChar; // 2-4 // out // Character used if a glyph isn't found (U+FFFD, '?') ImU8 Used8kPagesMap[(IM_UNICODE_CODEPOINT_MAX+1)/8192/8]; // 1 bytes if ImWchar=ImWchar16, 16 bytes if ImWchar==ImWchar32. Store 1-bit for each block of 4K codepoints that has one active glyph. This is mainly used to facilitate iterations across all used codepoints. @@ -3805,17 +3868,17 @@ struct ImFont IMGUI_API ImFont(); IMGUI_API ~ImFont(); IMGUI_API bool IsGlyphInFont(ImWchar c); - bool IsLoaded() const { return ContainerAtlas != NULL; } + bool IsLoaded() const { return OwnerAtlas != NULL; } const char* GetDebugName() const { return Sources.Size ? Sources[0]->Name : ""; } // Fill ImFontConfig::Name. // [Internal] Don't use! // 'max_width' stops rendering after a certain width (could be turned into a 2d size). FLT_MAX to disable. // 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable. IMGUI_API ImFontBaked* GetFontBaked(float font_size, float font_weight, float density = -1.0f); // Get or create baked data for given size - IMGUI_API ImVec2 CalcTextSizeA(float size, float weight, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** remaining = NULL); // utf8 + IMGUI_API ImVec2 CalcTextSizeA(float size, float weight, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** out_remaining = NULL); IMGUI_API const char* CalcWordWrapPosition(float size, float weight, const char* text, const char* text_end, float wrap_width); IMGUI_API void RenderChar(ImDrawList* draw_list, float size, float weight, const ImVec2& pos, ImU32 col, ImWchar c, const ImVec4* cpu_fine_clip = NULL); - IMGUI_API void RenderText(ImDrawList* draw_list, float size, float weight, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, bool cpu_fine_clip = false); + IMGUI_API void RenderText(ImDrawList* draw_list, float size, float weight, 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, DefaultWeight, text, text_end, wrap_width); } #endif @@ -3905,7 +3968,7 @@ struct ImGuiPlatformIO void* Platform_ClipboardUserData; // Optional: Open link/folder/file in OS Shell - // (default to use ShellExecuteW() on Windows, system() on Linux/Mac) + // (default to use ShellExecuteW() on Windows, system() on Linux/Mac. expected to return false on failure, but some platforms may always return true) bool (*Platform_OpenInShellFn)(ImGuiContext* ctx, const char* path); void* Platform_OpenInShellUserData; @@ -3937,6 +4000,13 @@ struct ImGuiPlatformIO // Textures list (the list is updated by calling ImGui::EndFrame or ImGui::Render) // The ImGui_ImplXXXX_RenderDrawData() function of each backend generally access this via ImDrawData::Textures which points to this. The array is available here mostly because backends will want to destroy textures on shutdown. ImVector Textures; // List of textures used by Dear ImGui (most often 1) + contents of external texture list is automatically appended into this. + + //------------------------------------------------------------------ + // Functions + //------------------------------------------------------------------ + + IMGUI_API void ClearPlatformHandlers(); // Clear all Platform_XXX fields. Typically called on Platform Backend shutdown. + IMGUI_API void ClearRendererHandlers(); // Clear all Renderer_XXX fields. Typically called on Renderer Backend shutdown. }; // (Optional) Support for IME (Input Method Editor) via the platform_io.Platform_SetImeDataFn() function. Handler is called during EndFrame(). @@ -3961,30 +4031,30 @@ struct ImGuiPlatformImeData namespace ImGui { // OBSOLETED in 1.92.0 (from June 2025) - static inline void PushFont(ImFont* font) { PushFont(font, font ? font->LegacySize : 0.0f, font ? font->DefaultWeight : -1.0f); } + inline void PushFont(ImFont* font) { PushFont(font, font ? font->LegacySize : 0.0f, font ? font->DefaultWeight : -1.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. // OBSOLETED in 1.91.9 (from February 2025) IMGUI_API void Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col); // <-- 'border_col' was removed in favor of ImGuiCol_ImageBorder. If you use 'tint_col', use ImageWithBg() instead. // OBSOLETED in 1.91.0 (from July 2024) - static inline void PushButtonRepeat(bool repeat) { PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat); } - static inline void PopButtonRepeat() { PopItemFlag(); } - static inline void PushTabStop(bool tab_stop) { PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); } - static inline void PopTabStop() { PopItemFlag(); } + inline void PushButtonRepeat(bool repeat) { PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat); } + inline void PopButtonRepeat() { PopItemFlag(); } + inline void PushTabStop(bool tab_stop) { PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); } + inline void PopTabStop() { PopItemFlag(); } IMGUI_API ImVec2 GetContentRegionMax(); // Content boundaries max (e.g. window boundaries including scrolling, or current column boundaries). You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()! IMGUI_API ImVec2 GetWindowContentRegionMin(); // Content boundaries min for the window (roughly (0,0)-Scroll), in window-local coordinates. You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()! IMGUI_API ImVec2 GetWindowContentRegionMax(); // Content boundaries max for the window (roughly (0,0)+Size-Scroll), in window-local coordinates. You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()! // OBSOLETED in 1.90.0 (from September 2023) - static inline bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags window_flags = 0) { return BeginChild(id, size, ImGuiChildFlags_FrameStyle, window_flags); } - static inline void EndChildFrame() { EndChild(); } - //static inline bool BeginChild(const char* str_id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags){ return BeginChild(str_id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Borders - //static inline bool BeginChild(ImGuiID id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags) { return BeginChild(id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Borders - static inline void ShowStackToolWindow(bool* p_open = NULL) { ShowIDStackToolWindow(p_open); } + inline bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags window_flags = 0) { return BeginChild(id, size, ImGuiChildFlags_FrameStyle, window_flags); } + inline void EndChildFrame() { EndChild(); } + //inline bool BeginChild(const char* str_id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags){ return BeginChild(str_id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Borders + //inline bool BeginChild(ImGuiID id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags) { return BeginChild(id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Borders + inline void ShowStackToolWindow(bool* p_open = NULL) { ShowIDStackToolWindow(p_open); } IMGUI_API bool Combo(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int popup_max_height_in_items = -1); IMGUI_API bool ListBox(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int height_in_items = -1); - // OBSOLETED in 1.89.7 (from June 2023) - IMGUI_API void SetItemAllowOverlap(); // Use SetNextItemAllowOverlap() before item. // Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE) + // OBSOLETED in 1.89.7 (from June 2023) + //IMGUI_API void SetItemAllowOverlap(); // Use SetNextItemAllowOverlap() _before_ item. //-- OBSOLETED in 1.89.4 (from March 2023) //static inline void PushAllowKeyboardFocus(bool tab_stop) { PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); } //static inline void PopAllowKeyboardFocus() { PopItemFlag(); } @@ -4050,7 +4120,7 @@ namespace ImGui //static inline void SetScrollPosHere() { SetScrollHere(); } // OBSOLETED in 1.42 } -//-- OBSOLETED in 1.92.x: ImFontAtlasCustomRect becomes ImTextureRect +//-- OBSOLETED in 1.92.0: ImFontAtlasCustomRect becomes ImTextureRect // - ImFontAtlasCustomRect::X,Y --> ImTextureRect::x,y // - ImFontAtlasCustomRect::Width,Height --> ImTextureRect::w,h // - ImFontAtlasCustomRect::GlyphColored --> if you need to write to this, instead you can write to 'font->Glyphs.back()->Colored' after calling AddCustomRectFontGlyph() @@ -4086,7 +4156,7 @@ typedef ImFontAtlasRect ImFontAtlasCustomRect; //}; // RENAMED and MERGED both ImGuiKey_ModXXX and ImGuiModFlags_XXX into ImGuiMod_XXX (from September 2022) -// RENAMED ImGuiKeyModFlags -> ImGuiModFlags in 1.88 (from April 2022). Exceptionally commented out ahead of obscolescence schedule to reduce confusion and because they were not meant to be used in the first place. +// RENAMED ImGuiKeyModFlags -> ImGuiModFlags in 1.88 (from April 2022). Exceptionally commented out ahead of obsolescence schedule to reduce confusion and because they were not meant to be used in the first place. //typedef ImGuiKeyChord ImGuiModFlags; // == int. We generally use ImGuiKeyChord to mean "a ImGuiKey or-ed with any number of ImGuiMod_XXX value", so you may store mods in there. //enum ImGuiModFlags_ { ImGuiModFlags_None = 0, ImGuiModFlags_Ctrl = ImGuiMod_Ctrl, ImGuiModFlags_Shift = ImGuiMod_Shift, ImGuiModFlags_Alt = ImGuiMod_Alt, ImGuiModFlags_Super = ImGuiMod_Super }; //typedef ImGuiKeyChord ImGuiKeyModFlags; // == int @@ -4096,6 +4166,8 @@ typedef ImFontAtlasRect ImFontAtlasCustomRect; #endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +#define IM_ARRAYSIZE IM_COUNTOF // RENAMED IN 1.92.6: IM_ARRAYSIZE -> IM_COUNTOF + // RENAMED IMGUI_DISABLE_METRICS_WINDOW > IMGUI_DISABLE_DEBUG_TOOLS in 1.88 (from June 2022) #ifdef IMGUI_DISABLE_METRICS_WINDOW #error IMGUI_DISABLE_METRICS_WINDOW was renamed to IMGUI_DISABLE_DEBUG_TOOLS, please use new name. diff --git a/dep/imgui/include/imgui_freetype.h b/dep/imgui/include/imgui_freetype.h index 4f7306790..85313699d 100644 --- a/dep/imgui/include/imgui_freetype.h +++ b/dep/imgui/include/imgui_freetype.h @@ -8,7 +8,7 @@ // Usage: // - Add '#define IMGUI_ENABLE_FREETYPE' in your imconfig to automatically enable support // for imgui_freetype in imgui. It is equivalent to selecting the default loader with: -// io.Fonts.FontLoader = ImGuiFreeType::GetFontLoader() +// io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader()) // Optional support for OpenType SVG fonts: // - Add '#define IMGUI_ENABLE_FREETYPE_PLUTOSVG' to use plutosvg (not provided). See #7927. @@ -62,7 +62,7 @@ namespace ImGuiFreeType { // This is automatically assigned when using '#define IMGUI_ENABLE_FREETYPE'. // If you need to dynamically select between multiple builders: - // - you can manually assign this builder with 'atlas->FontLoader = ImGuiFreeType::GetFontLoader()' + // - you can manually assign this builder with 'atlas->SetFontLoader(ImGuiFreeType::GetFontLoader())' // - prefer deep-copying this into your own ImFontLoader instance if you use hot-reloading that messes up static data. IMGUI_API const ImFontLoader* GetFontLoader(); @@ -75,7 +75,7 @@ namespace ImGuiFreeType // Obsolete names (will be removed) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - //IMGUI_API const ImFontBuilderIO* GetBuilderForFreeType(); // Renamed/changed in 1.92. Change 'io.Fonts->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()' to 'io.Fonts.FontLoader = ImGuiFreeType::GetFontLoader()' if you need runtime selection. + //IMGUI_API const ImFontBuilderIO* GetBuilderForFreeType(); // Renamed/changed in 1.92. Change 'io.Fonts->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()' to 'io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader())' if you need runtime selection. //static inline bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int flags = 0) { atlas->FontBuilderIO = GetBuilderForFreeType(); atlas->FontLoaderFlags = flags; return atlas->Build(); } // Prefer using '#define IMGUI_ENABLE_FREETYPE' #endif } diff --git a/dep/imgui/include/imgui_internal.h b/dep/imgui/include/imgui_internal.h index ee42e9b0a..700b91ad4 100644 --- a/dep/imgui/include/imgui_internal.h +++ b/dep/imgui/include/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.92.0 +// dear imgui, v1.92.6 WIP // (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. @@ -77,8 +77,8 @@ Index of this file: #ifdef _MSC_VER #pragma warning (push) #pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport) -#pragma warning (disable: 26812) // The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer) #pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6). +#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types #endif @@ -193,6 +193,7 @@ enum ImGuiLocKey : int; // -> enum ImGuiLocKey // E typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical // Flags +typedef int ImDrawTextFlags; // -> enum ImDrawTextFlags_ // Flags: for ImTextCalcWordWrapPositionEx() typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later) typedef int ImGuiDebugLogFlags; // -> enum ImGuiDebugLogFlags_ // Flags: for ShowDebugLogWindow(), g.DebugLogFlags typedef int ImGuiFocusRequestFlags; // -> enum ImGuiFocusRequestFlags_ // Flags: for FocusWindow() @@ -208,6 +209,7 @@ typedef int ImGuiSeparatorFlags; // -> enum ImGuiSeparatorFlags_ // F typedef int ImGuiTextFlags; // -> enum ImGuiTextFlags_ // Flags: for TextEx() typedef int ImGuiTooltipFlags; // -> enum ImGuiTooltipFlags_ // Flags: for BeginTooltipEx() typedef int ImGuiTypingSelectFlags; // -> enum ImGuiTypingSelectFlags_ // Flags: for GetTypingSelectRequest() +typedef int ImGuiWindowBgClickFlags; // -> enum ImGuiWindowBgClickFlags_ // Flags: for overriding behavior of clicking on window background/void. typedef int ImGuiWindowRefreshFlags; // -> enum ImGuiWindowRefreshFlags_ // Flags: for SetNextWindowRefreshPolicy() // Table column indexing @@ -274,12 +276,10 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer #define IM_MEMALIGN(_OFF,_ALIGN) (((_OFF) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1)) // Memory align e.g. IM_ALIGN(0,4)=0, IM_ALIGN(1,4)=4, IM_ALIGN(4,4)=4, IM_ALIGN(5,4)=8 #define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose #define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255 -#define IM_TRUNC(_VAL) ((float)(int)(_VAL)) // ImTrunc() is not inlined in MSVC debug builds -#define IM_ROUND(_VAL) ((float)(int)((_VAL) + 0.5f)) // -#define IM_STRINGIFY_HELPER(_X) #_X -#define IM_STRINGIFY(_X) IM_STRINGIFY_HELPER(_X) // Preprocessor idiom to stringify e.g. an integer. +#define IM_TRUNC(_VAL) ((float)(int)(_VAL)) // Positive values only! ImTrunc() is not inlined in MSVC debug builds +#define IM_ROUND(_VAL) ((float)(int)((_VAL) + 0.5f)) // Positive values only! #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS -#define IM_FLOOR IM_TRUNC +#define IM_FLOOR IM_TRUNC // [OBSOLETE] Renamed in 1.90.0 (Sept 2023) #endif // Hint for branch prediction @@ -366,20 +366,21 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer // Helpers: Hashing IMGUI_API ImGuiID ImHashData(const void* data, size_t data_size, ImGuiID seed = 0); IMGUI_API ImGuiID ImHashStr(const char* data, size_t data_size = 0, ImGuiID seed = 0); +IMGUI_API const char* ImHashSkipUncontributingPrefix(const char* label); // Helpers: Sorting #ifndef ImQsort -static inline void ImQsort(void* base, size_t count, size_t size_of_element, int(IMGUI_CDECL *compare_func)(void const*, void const*)) { if (count > 1) qsort(base, count, size_of_element, compare_func); } +inline void ImQsort(void* base, size_t count, size_t size_of_element, int(IMGUI_CDECL *compare_func)(void const*, void const*)) { if (count > 1) qsort(base, count, size_of_element, compare_func); } #endif // Helpers: Color Blending IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b); // Helpers: Bit manipulation -static inline bool ImIsPowerOfTwo(int v) { return v != 0 && (v & (v - 1)) == 0; } -static inline bool ImIsPowerOfTwo(ImU64 v) { return v != 0 && (v & (v - 1)) == 0; } -static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } -static inline unsigned int ImCountSetBits(unsigned int v) { unsigned int count = 0; while (v > 0) { v = v & (v - 1); count++; } return count; } +inline bool ImIsPowerOfTwo(int v) { return v != 0 && (v & (v - 1)) == 0; } +inline bool ImIsPowerOfTwo(ImU64 v) { return v != 0 && (v & (v - 1)) == 0; } +inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } +inline unsigned int ImCountSetBits(unsigned int v) { unsigned int count = 0; while (v > 0) { v = v & (v - 1); count++; } return count; } // Helpers: String #define ImStrlen strlen @@ -398,10 +399,10 @@ IMGUI_API const char* ImStrSkipBlank(const char* str); IMGUI_API int ImStrlenW(const ImWchar* str); // Computer string length (ImWchar string) IMGUI_API const char* ImStrbol(const char* buf_mid_line, const char* buf_begin); // Find beginning-of-line IM_MSVC_RUNTIME_CHECKS_OFF -static inline char ImToUpper(char c) { return (c >= 'a' && c <= 'z') ? c &= ~32 : c; } -static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; } -static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; } -static inline bool ImCharIsXdigitA(char c) { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); } +inline char ImToUpper(char c) { return (c >= 'a' && c <= 'z') ? c &= ~32 : c; } +inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; } +inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; } +inline bool ImCharIsXdigitA(char c) { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); } IM_MSVC_RUNTIME_CHECKS_RESTORE // Helpers: Formatting @@ -417,25 +418,48 @@ IMGUI_API const char* ImParseFormatSanitizeForScanning(const char* fmt_in, cha IMGUI_API int ImParseFormatPrecision(const char* format, int default_value); // Helpers: UTF-8 <> wchar conversions -IMGUI_API const char* ImTextCharToUtf8(char out_buf[5], unsigned int c); // return out_buf +IMGUI_API int ImTextCharToUtf8(char out_buf[5], unsigned int c); // return output UTF-8 bytes count IMGUI_API int ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end); // return output UTF-8 bytes count IMGUI_API int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end); // read one character. return input UTF-8 bytes count IMGUI_API int ImTextStrFromUtf8(ImWchar* out_buf, int out_buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL); // return input UTF-8 bytes count IMGUI_API int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end); // return number of UTF-8 code-points (NOT bytes count) IMGUI_API int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end); // return number of bytes to express one char in UTF-8 IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string in UTF-8 -IMGUI_API const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_text_curr); // return previous UTF-8 code-point. +IMGUI_API const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_p); // return previous UTF-8 code-point. +IMGUI_API const char* ImTextFindValidUtf8CodepointEnd(const char* in_text_start, const char* in_text_end, const char* in_p); // return previous UTF-8 code-point if 'in_p' is not the end of a valid one. IMGUI_API int ImTextCountLines(const char* in_text, const char* in_text_end); // return number of lines taken by text. trailing carriage return doesn't count as an extra line. +// Helpers: High-level text functions (DO NOT USE!!! THIS IS A MINIMAL SUBSET OF LARGER UPCOMING CHANGES) +enum ImDrawTextFlags_ +{ + ImDrawTextFlags_None = 0, + ImDrawTextFlags_CpuFineClip = 1 << 0, // Must be == 1/true for legacy with 'bool cpu_fine_clip' arg to RenderText() + ImDrawTextFlags_WrapKeepBlanks = 1 << 1, + ImDrawTextFlags_StopOnNewLine = 1 << 2, +}; +IMGUI_API ImVec2 ImFontCalcTextSizeEx(ImFont* font, float size, float weight, float max_width, float wrap_width, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining, ImVec2* out_offset, ImDrawTextFlags flags); +IMGUI_API const char* ImFontCalcWordWrapPositionEx(ImFont* font, float size, float weight, const char* text, const char* text_end, float wrap_width, ImDrawTextFlags flags = 0); +IMGUI_API const char* ImTextCalcWordWrapNextLineStart(const char* text, const char* text_end, ImDrawTextFlags flags = 0); // trim trailing space and find beginning of next line + +// Character classification for word-wrapping logic +enum ImWcharClass +{ + ImWcharClass_Blank, ImWcharClass_Punct, ImWcharClass_Other +}; +IMGUI_API void ImTextInitClassifiers(); +IMGUI_API void ImTextClassifierClear(ImU32* bits, unsigned int codepoint_min, unsigned int codepoint_end, ImWcharClass char_class); +IMGUI_API void ImTextClassifierSetCharClass(ImU32* bits, unsigned int codepoint_min, unsigned int codepoint_end, ImWcharClass char_class, unsigned int c); +IMGUI_API void ImTextClassifierSetCharClassFromStr(ImU32* bits, unsigned int codepoint_min, unsigned int codepoint_end, ImWcharClass char_class, const char* s); + // Helpers: File System #ifdef IMGUI_DISABLE_FILE_FUNCTIONS #define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS typedef void* ImFileHandle; -static inline ImFileHandle ImFileOpen(const char*, const char*) { return NULL; } -static inline bool ImFileClose(ImFileHandle) { return false; } -static inline ImU64 ImFileGetSize(ImFileHandle) { return (ImU64)-1; } -static inline ImU64 ImFileRead(void*, ImU64, ImU64, ImFileHandle) { return 0; } -static inline ImU64 ImFileWrite(const void*, ImU64, ImU64, ImFileHandle) { return 0; } +inline ImFileHandle ImFileOpen(const char*, const char*) { return NULL; } +inline bool ImFileClose(ImFileHandle) { return false; } +inline ImU64 ImFileGetSize(ImFileHandle) { return (ImU64)-1; } +inline ImU64 ImFileRead(void*, ImU64, ImU64, ImFileHandle) { return 0; } +inline ImU64 ImFileWrite(const void*, ImU64, ImU64, ImFileHandle) { return 0; } #endif #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS typedef FILE* ImFileHandle; @@ -462,56 +486,56 @@ IM_MSVC_RUNTIME_CHECKS_OFF #define ImAtan2(Y, X) atan2f((Y), (X)) #define ImAtof(STR) atof(STR) #define ImCeil(X) ceilf(X) -static inline float ImPow(float x, float y) { return powf(x, y); } // DragBehaviorT/SliderBehaviorT uses ImPow with either float/double and need the precision -static inline double ImPow(double x, double y) { return pow(x, y); } -static inline float ImLog(float x) { return logf(x); } // DragBehaviorT/SliderBehaviorT uses ImLog with either float/double and need the precision -static inline double ImLog(double x) { return log(x); } -static inline int ImAbs(int x) { return x < 0 ? -x : x; } -static inline float ImAbs(float x) { return fabsf(x); } -static inline double ImAbs(double x) { return fabs(x); } -static inline float ImSign(float x) { return (x < 0.0f) ? -1.0f : (x > 0.0f) ? 1.0f : 0.0f; } // Sign operator - returns -1, 0 or 1 based on sign of argument -static inline double ImSign(double x) { return (x < 0.0) ? -1.0 : (x > 0.0) ? 1.0 : 0.0; } +inline float ImPow(float x, float y) { return powf(x, y); } // DragBehaviorT/SliderBehaviorT uses ImPow with either float/double and need the precision +inline double ImPow(double x, double y) { return pow(x, y); } +inline float ImLog(float x) { return logf(x); } // DragBehaviorT/SliderBehaviorT uses ImLog with either float/double and need the precision +inline double ImLog(double x) { return log(x); } +inline int ImAbs(int x) { return x < 0 ? -x : x; } +inline float ImAbs(float x) { return fabsf(x); } +inline double ImAbs(double x) { return fabs(x); } +inline float ImSign(float x) { return (x < 0.0f) ? -1.0f : (x > 0.0f) ? 1.0f : 0.0f; } // Sign operator - returns -1, 0 or 1 based on sign of argument +inline double ImSign(double x) { return (x < 0.0) ? -1.0 : (x > 0.0) ? 1.0 : 0.0; } #ifdef IMGUI_ENABLE_SSE -static inline float ImRsqrt(float x) { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); } +inline float ImRsqrt(float x) { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); } #else -static inline float ImRsqrt(float x) { return 1.0f / sqrtf(x); } +inline float ImRsqrt(float x) { return 1.0f / sqrtf(x); } #endif -static inline double ImRsqrt(double x) { return 1.0 / sqrt(x); } +inline double ImRsqrt(double x) { return 1.0 / sqrt(x); } #endif // - ImMin/ImMax/ImClamp/ImLerp/ImSwap are used by widgets which support variety of types: signed/unsigned int/long long float/double // (Exceptionally using templates here but we could also redefine them for those types) -template static inline T ImMin(T lhs, T rhs) { return lhs < rhs ? lhs : rhs; } -template static inline T ImMax(T lhs, T rhs) { return lhs >= rhs ? lhs : rhs; } -template static inline T ImClamp(T v, T mn, T mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } -template static inline T ImLerp(T a, T b, float t) { return (T)(a + (b - a) * t); } -template static inline void ImSwap(T& a, T& b) { T tmp = a; a = b; b = tmp; } -template static inline T ImAddClampOverflow(T a, T b, T mn, T mx) { if (b < 0 && (a < mn - b)) return mn; if (b > 0 && (a > mx - b)) return mx; return a + b; } -template static inline T ImSubClampOverflow(T a, T b, T mn, T mx) { if (b > 0 && (a < mn + b)) return mn; if (b < 0 && (a > mx + b)) return mx; return a - b; } +template T ImMin(T lhs, T rhs) { return lhs < rhs ? lhs : rhs; } +template T ImMax(T lhs, T rhs) { return lhs >= rhs ? lhs : rhs; } +template T ImClamp(T v, T mn, T mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } +template T ImLerp(T a, T b, float t) { return (T)(a + (b - a) * t); } +template void ImSwap(T& a, T& b) { T tmp = a; a = b; b = tmp; } +template T ImAddClampOverflow(T a, T b, T mn, T mx) { if (b < 0 && (a < mn - b)) return mn; if (b > 0 && (a > mx - b)) return mx; return a + b; } +template T ImSubClampOverflow(T a, T b, T mn, T mx) { if (b > 0 && (a < mn + b)) return mn; if (b < 0 && (a > mx + b)) return mx; return a - b; } // - Misc maths helpers -static inline ImVec2 ImMin(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x < rhs.x ? lhs.x : rhs.x, lhs.y < rhs.y ? lhs.y : rhs.y); } -static inline ImVec2 ImMax(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x >= rhs.x ? lhs.x : rhs.x, lhs.y >= rhs.y ? lhs.y : rhs.y); } -static inline ImVec2 ImClamp(const ImVec2& v, const ImVec2&mn, const ImVec2&mx) { return ImVec2((v.x < mn.x) ? mn.x : (v.x > mx.x) ? mx.x : v.x, (v.y < mn.y) ? mn.y : (v.y > mx.y) ? mx.y : v.y); } -static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, float t) { return ImVec2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t); } -static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t) { return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); } -static inline ImVec4 ImLerp(const ImVec4& a, const ImVec4& b, float t) { return ImVec4(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t, a.w + (b.w - a.w) * t); } -static inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; } -static inline float ImLengthSqr(const ImVec2& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y); } -static inline float ImLengthSqr(const ImVec4& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y) + (lhs.z * lhs.z) + (lhs.w * lhs.w); } -static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImRsqrt(d); return fail_value; } -static inline float ImTrunc(float f) { return (float)(int)(f); } -static inline ImVec2 ImTrunc(const ImVec2& v) { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); } -static inline float ImFloor(float f) { return (float)((f >= 0 || (float)(int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf() -static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2(ImFloor(v.x), ImFloor(v.y)); } -static inline float ImTrunc64(float f) { return (float)(ImS64)(f); } -static inline float ImRound64(float f) { return (float)(ImS64)(f + 0.5f); } -static inline int ImModPositive(int a, int b) { return (a + b) % b; } -static inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; } -static 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); } -static inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; } -static inline float ImLinearRemapClamp(float s0, float s1, float d0, float d1, float x) { return ImSaturate((x - s0) / (s1 - s0)) * (d1 - d0) + d0; } -static inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); } -static inline bool ImIsFloatAboveGuaranteedIntegerPrecision(float f) { return f <= -16777216 || f >= 16777216; } -static inline float ImExponentialMovingAverage(float avg, float sample, int n) { avg -= avg / n; avg += sample / n; return avg; } +inline ImVec2 ImMin(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x < rhs.x ? lhs.x : rhs.x, lhs.y < rhs.y ? lhs.y : rhs.y); } +inline ImVec2 ImMax(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x >= rhs.x ? lhs.x : rhs.x, lhs.y >= rhs.y ? lhs.y : rhs.y); } +inline ImVec2 ImClamp(const ImVec2& v, const ImVec2&mn, const ImVec2&mx){ return ImVec2((v.x < mn.x) ? mn.x : (v.x > mx.x) ? mx.x : v.x, (v.y < mn.y) ? mn.y : (v.y > mx.y) ? mx.y : v.y); } +inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, float t) { return ImVec2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t); } +inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t) { return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); } +inline ImVec4 ImLerp(const ImVec4& a, const ImVec4& b, float t) { return ImVec4(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t, a.w + (b.w - a.w) * t); } +inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; } +inline float ImLengthSqr(const ImVec2& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y); } +inline float ImLengthSqr(const ImVec4& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y) + (lhs.z * lhs.z) + (lhs.w * lhs.w); } +inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImRsqrt(d); return fail_value; } +inline float ImTrunc(float f) { return (float)(int)(f); } +inline ImVec2 ImTrunc(const ImVec2& v) { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); } +inline float ImFloor(float f) { return (float)((f >= 0 || (float)(int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf() +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 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); } +inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; } +inline float ImLinearRemapClamp(float s0, float s1, float d0, float d1, float x) { return ImSaturate((x - s0) / (s1 - s0)) * (d1 - d0) + d0; } +inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); } +inline bool ImIsFloatAboveGuaranteedIntegerPrecision(float f) { return f <= -16777216 || f >= 16777216; } +inline float ImExponentialMovingAverage(float avg, float sample, int n){ avg -= avg / n; avg += sample / n; return avg; } IM_MSVC_RUNTIME_CHECKS_RESTORE // Helpers: Geometry @@ -587,9 +611,9 @@ struct IMGUI_API ImRect void TranslateY(float dy) { Min.y += dy; Max.y += dy; } void ClipWith(const ImRect& r) { Min = ImMax(Min, r.Min); Max = ImMin(Max, r.Max); } // Simple version, may lead to an inverted rectangle, which is fine for Contains/Overlaps test but not for display. void ClipWithFull(const ImRect& r) { Min = ImClamp(Min, r.Min, r.Max); Max = ImClamp(Max, r.Min, r.Max); } // Full version, ensure both points are fully clipped. - void Floor() { Min.x = IM_TRUNC(Min.x); Min.y = IM_TRUNC(Min.y); Max.x = IM_TRUNC(Max.x); Max.y = IM_TRUNC(Max.y); } bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; } ImVec4 ToVec4() const { return ImVec4(Min.x, Min.y, Max.x, Max.y); } + const ImVec4& AsVec4() const { return *(const ImVec4*)&Min.x; } }; // Helper: ImBitArray @@ -620,15 +644,15 @@ typedef ImU32* ImBitArrayPtr; // Name for use in structs template struct ImBitArray { - ImU32 Storage[(BITCOUNT + 31) >> 5]; + ImU32 Data[(BITCOUNT + 31) >> 5]; ImBitArray() { ClearAllBits(); } - void ClearAllBits() { memset(Storage, 0, sizeof(Storage)); } - void SetAllBits() { memset(Storage, 255, sizeof(Storage)); } - bool TestBit(int n) const { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return IM_BITARRAY_TESTBIT(Storage, n); } - void SetBit(int n) { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArraySetBit(Storage, n); } - void ClearBit(int n) { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArrayClearBit(Storage, n); } - void SetBitRange(int n, int n2) { n += OFFSET; n2 += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT && n2 > n && n2 <= BITCOUNT); ImBitArraySetBitRange(Storage, n, n2); } // Works on range [n..n2) - bool operator[](int n) const { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return IM_BITARRAY_TESTBIT(Storage, n); } + void ClearAllBits() { memset(Data, 0, sizeof(Data)); } + void SetAllBits() { memset(Data, 255, sizeof(Data)); } + bool TestBit(int n) const { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return IM_BITARRAY_TESTBIT(Data, n); } + void SetBit(int n) { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArraySetBit(Data, n); } + void ClearBit(int n) { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArrayClearBit(Data, n); } + void SetBitRange(int n, int n2) { n += OFFSET; n2 += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT && n2 > n && n2 <= BITCOUNT); ImBitArraySetBitRange(Data, n, n2); } // Works on range [n..n2) + bool operator[](int n) const { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return IM_BITARRAY_TESTBIT(Data, n); } }; // Helper: ImBitVector @@ -696,10 +720,10 @@ struct ImSpanAllocator }; // Helper: ImStableVector<> -// Allocating chunks of BLOCK_SIZE items. Objects pointers are never invalidated when growing, only by clear(). +// Allocating chunks of BLOCKSIZE items. Objects pointers are never invalidated when growing, only by clear(). // Important: does not destruct anything! // Implemented only the minimum set of functions we need for it. -template +template struct ImStableVector { int Size = 0; @@ -713,19 +737,19 @@ struct ImStableVector inline void resize(int new_size) { if (new_size > Capacity) reserve(new_size); Size = new_size; } inline void reserve(int new_cap) { - new_cap = IM_MEMALIGN(new_cap, BLOCK_SIZE); - int old_count = Capacity / BLOCK_SIZE; - int new_count = new_cap / BLOCK_SIZE; + new_cap = IM_MEMALIGN(new_cap, BLOCKSIZE); + int old_count = Capacity / BLOCKSIZE; + int new_count = new_cap / BLOCKSIZE; if (new_count <= old_count) return; Blocks.resize(new_count); for (int n = old_count; n < new_count; n++) - Blocks[n] = (T*)IM_ALLOC(sizeof(T) * BLOCK_SIZE); + Blocks[n] = (T*)IM_ALLOC(sizeof(T) * BLOCKSIZE); Capacity = new_cap; } - inline T& operator[](int i) { IM_ASSERT(i >= 0 && i < Size); return Blocks[i / BLOCK_SIZE][i % BLOCK_SIZE]; } - inline const T& operator[](int i) const { IM_ASSERT(i >= 0 && i < Size); return Blocks[i / BLOCK_SIZE][i % BLOCK_SIZE]; } - inline T* push_back(const T& v) { int i = Size; IM_ASSERT(i >= 0); if (Size == Capacity) reserve(Capacity + BLOCK_SIZE); void* ptr = &Blocks[i / BLOCK_SIZE][i % BLOCK_SIZE]; memcpy(ptr, &v, sizeof(v)); Size++; return (T*)ptr; } + inline T& operator[](int i) { IM_ASSERT(i >= 0 && i < Size); return Blocks[i / BLOCKSIZE][i % BLOCKSIZE]; } + inline const T& operator[](int i) const { IM_ASSERT(i >= 0 && i < Size); return Blocks[i / BLOCKSIZE][i % BLOCKSIZE]; } + inline T* push_back(const T& v) { int i = Size; IM_ASSERT(i >= 0); if (Size == Capacity) reserve(Capacity + BLOCKSIZE); void* ptr = &Blocks[i / BLOCKSIZE][i % BLOCKSIZE]; memcpy(ptr, &v, sizeof(v)); Size++; return (T*)ptr; } }; // Helper: ImPool<> @@ -788,13 +812,13 @@ struct ImChunkStream // Maintain a line index for a text buffer. This is a strong candidate to be moved into the public API. struct ImGuiTextIndex { - ImVector LineOffsets; + ImVector Offsets; int EndOffset = 0; // Because we don't own text buffer we need to maintain EndOffset (may bake in LineOffsets?) - void clear() { LineOffsets.clear(); EndOffset = 0; } - int size() { return LineOffsets.Size; } - const char* get_line_begin(const char* base, int n) { return base + LineOffsets[n]; } - const char* get_line_end(const char* base, int n) { return base + (n + 1 < LineOffsets.Size ? (LineOffsets[n + 1] - 1) : EndOffset); } + void clear() { Offsets.clear(); EndOffset = 0; } + int size() { return Offsets.Size; } + const char* get_line_begin(const char* base, int n) { return base + (Offsets.Size != 0 ? Offsets[n] : 0); } + const char* get_line_end(const char* base, int n) { return base + (n + 1 < Offsets.Size ? (Offsets[n + 1] - 1) : EndOffset); } void append(const char* base, int old_size, int new_size); }; @@ -942,7 +966,6 @@ enum ImGuiDataTypePrivate_ enum ImGuiItemFlagsPrivate_ { // Controlled by user - ImGuiItemFlags_Disabled = 1 << 10, // false // Disable interactions (DOES NOT affect visuals. DO NOT mix direct use of this with BeginDisabled(). See BeginDisabled()/EndDisabled() for full disable feature, and github #211). ImGuiItemFlags_ReadOnly = 1 << 11, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. ImGuiItemFlags_MixedValue = 1 << 12, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets) ImGuiItemFlags_NoWindowHoverableCheck = 1 << 13, // false // Disable hoverable check in ItemHoverable() @@ -978,6 +1001,7 @@ enum ImGuiItemStatusFlags_ ImGuiItemStatusFlags_Visible = 1 << 8, // [WIP] Set when item is overlapping the current clipping rectangle (Used internally. Please don't use yet: API/system will change as we refactor Itemadd()). ImGuiItemStatusFlags_HasClipRect = 1 << 9, // g.LastItemData.ClipRect is valid. ImGuiItemStatusFlags_HasShortcut = 1 << 10, // g.LastItemData.Shortcut valid. Set by SetNextItemShortcut() -> ItemAdd(). + //ImGuiItemStatusFlags_FocusedByTabbing = 1 << 8, // Removed IN 1.90.1 (Dec 2023). The trigger is part of g.NavActivateId. See commit 54c1bdeceb. // Additional status + semantic for ImGuiTestEngine #ifdef IMGUI_ENABLE_TEST_ENGINE @@ -1030,6 +1054,7 @@ enum ImGuiButtonFlagsPrivate_ ImGuiButtonFlags_NoFocus = 1 << 22, // [EXPERIMENTAL: Not very well specced]. Don't focus parent window when clicking. ImGuiButtonFlags_PressedOnMask_ = ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_PressedOnDragDropHold, ImGuiButtonFlags_PressedOnDefault_ = ImGuiButtonFlags_PressedOnClickRelease, + //ImGuiButtonFlags_NoKeyModifiers = ImGuiButtonFlags_NoKeyModsAllowed, // Renamed in 1.91.4 }; // Extend ImGuiComboFlags_ @@ -1050,7 +1075,6 @@ enum ImGuiSelectableFlagsPrivate_ { // NB: need to be in sync with last value of ImGuiSelectableFlags_ ImGuiSelectableFlags_NoHoldingActiveID = 1 << 20, - ImGuiSelectableFlags_SelectOnNav = 1 << 21, // (WIP) Auto-select when moved into. This is not exposed in public API as to handle multi-select and modifiers we will need user to explicitly control focus scope. May be replaced with a BeginSelection() API. ImGuiSelectableFlags_SelectOnClick = 1 << 22, // Override button behavior to react on Click (default is Click+Release) ImGuiSelectableFlags_SelectOnRelease = 1 << 23, // Override button behavior to react on Release (default is Click+Release) ImGuiSelectableFlags_SpanAvailWidth = 1 << 24, // Span all avail width even if we declared less for layout purpose. FIXME: We may be able to remove this (added in 6251d379, 2bcafc86 for menus) @@ -1158,6 +1182,7 @@ struct IMGUI_API ImGuiGroupData ImVec2 BackupCurrLineSize; float BackupCurrLineTextBaseOffset; ImGuiID BackupActiveIdIsAlive; + bool BackupActiveIdHasBeenEditedThisFrame; bool BackupDeactivatedIdIsAlive; bool BackupHoveredIdIsAlive; bool BackupIsSameLine; @@ -1218,11 +1243,15 @@ struct IMGUI_API ImGuiInputTextState ImVector CallbackTextBackup; // temporary storage for callback to support automatic reconcile of undo-stack int BufCapacity; // end-user buffer capacity (include zero terminator) ImVec2 Scroll; // horizontal offset (managed manually) + vertical scrolling (pulled from child window's own Scroll.y) + int LineCount; // last line count (solely for debugging) + float WrapWidth; // word-wrapping width float CursorAnim; // timer for cursor blink, reset on every user action so the cursor reappears immediately bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!) + bool CursorCenterY; // set when we want scrolling to be centered over the cursor position (while resizing a word-wrapping field) bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection bool Edited; // edited this frame bool WantReloadUserBuf; // force a reload of user buf so it may be modified externally. may be automatic in future version. + ImS8 LastMoveDirectionLR; // ImGuiDir_Left or ImGuiDir_Right. track last movement direction so when cursor cross over a word-wrapping boundaries we can display it on either line depending on last move.s int ReloadSelectionStart; int ReloadSelectionEnd; @@ -1232,6 +1261,7 @@ struct IMGUI_API ImGuiInputTextState void ClearFreeMemory() { TextA.clear(); TextToRevertTo.clear(); } void OnKeyPressed(int key); // Cannot be inline because we call in code in stb_textedit.h implementation void OnCharPressed(unsigned int c); + float GetPreferredOffsetX() const; // Cursor & Selection void CursorAnimReset(); @@ -1262,6 +1292,12 @@ enum ImGuiWindowRefreshFlags_ // Refresh policy/frequency, Load Balancing etc. }; +enum ImGuiWindowBgClickFlags_ +{ + ImGuiWindowBgClickFlags_None = 0, + ImGuiWindowBgClickFlags_Move = 1 << 0, // Click on bg/void + drag to move window. Cleared by default when using io.ConfigWindowsMoveFromTitleBarOnly. +}; + enum ImGuiNextWindowDataFlags_ { ImGuiNextWindowDataFlags_None = 0, @@ -1308,12 +1344,13 @@ struct ImGuiNextWindowData enum ImGuiNextItemDataFlags_ { - ImGuiNextItemDataFlags_None = 0, - ImGuiNextItemDataFlags_HasWidth = 1 << 0, - ImGuiNextItemDataFlags_HasOpen = 1 << 1, - ImGuiNextItemDataFlags_HasShortcut = 1 << 2, - ImGuiNextItemDataFlags_HasRefVal = 1 << 3, - ImGuiNextItemDataFlags_HasStorageID = 1 << 4, + ImGuiNextItemDataFlags_None = 0, + ImGuiNextItemDataFlags_HasWidth = 1 << 0, + ImGuiNextItemDataFlags_HasOpen = 1 << 1, + ImGuiNextItemDataFlags_HasShortcut = 1 << 2, + ImGuiNextItemDataFlags_HasRefVal = 1 << 3, + ImGuiNextItemDataFlags_HasStorageID = 1 << 4, + ImGuiNextItemDataFlags_HasColorMarker = 1 << 5, }; struct ImGuiNextItemData @@ -1331,6 +1368,7 @@ struct ImGuiNextItemData ImU8 OpenCond; // Set by SetNextItemOpen() ImGuiDataTypeStorage RefVal; // Not exposed yet, for ImGuiInputTextFlags_ParseEmptyAsRefVal ImGuiID StorageId; // Set by SetNextItemStorageID() + ImU32 ColorMarker; // Set by SetNextItemColorMarker(). Not exposed yet, supported by DragScalar,SliderScalar and for ImGuiSliderFlags_ColorMarkers. ImGuiNextItemData() { memset(this, 0, sizeof(*this)); SelectionUserData = -1; } inline void ClearFlags() { HasFlags = ImGuiNextItemDataFlags_None; ItemFlags = ImGuiItemFlags_None; } // Also cleared manually by ItemAdd()! @@ -1487,7 +1525,7 @@ enum ImGuiInputEventType ImGuiInputEventType_COUNT }; -enum ImGuiInputSource +enum ImGuiInputSource : int { ImGuiInputSource_None = 0, ImGuiInputSource_Mouse, // Note: may be Mouse or TouchScreen or Pen. See io.MouseSource to distinguish them. @@ -1536,12 +1574,12 @@ struct ImGuiKeyRoutingData { ImGuiKeyRoutingIndex NextEntryIndex; ImU16 Mods; // Technically we'd only need 4-bits but for simplify we store ImGuiMod_ values which need 16-bits. - ImU8 RoutingCurrScore; // [DEBUG] For debug display - ImU8 RoutingNextScore; // Lower is better (0: perfect score) + ImU16 RoutingCurrScore; // [DEBUG] For debug display + ImU16 RoutingNextScore; // Lower is better (0: perfect score) ImGuiID RoutingCurr; ImGuiID RoutingNext; - ImGuiKeyRoutingData() { NextEntryIndex = -1; Mods = 0; RoutingCurrScore = RoutingNextScore = 255; RoutingCurr = RoutingNext = ImGuiKeyOwner_NoOwner; } + ImGuiKeyRoutingData() { NextEntryIndex = -1; Mods = 0; RoutingCurrScore = RoutingNextScore = 0; RoutingCurr = RoutingNext = ImGuiKeyOwner_NoOwner; } }; // Routing table: maintain a desired owner for each possible key-chord (key + mods), and setup owner in NewFrame() when mods are matching. @@ -1553,7 +1591,7 @@ struct ImGuiKeyRoutingTable ImVector EntriesNext; // Double-buffer to avoid reallocation (could use a shared buffer) ImGuiKeyRoutingTable() { Clear(); } - void Clear() { for (int n = 0; n < IM_ARRAYSIZE(Index); n++) Index[n] = -1; Entries.clear(); EntriesNext.clear(); } + void Clear() { for (int n = 0; n < IM_COUNTOF(Index); n++) Index[n] = -1; Entries.clear(); EntriesNext.clear(); } }; // This extends ImGuiKeyData but only for named keys (legacy keys don't support the new features) @@ -1650,8 +1688,9 @@ enum ImGuiActivateFlags_ ImGuiActivateFlags_PreferInput = 1 << 0, // Favor activation that requires keyboard text input (e.g. for Slider/Drag). Default for Enter key. ImGuiActivateFlags_PreferTweak = 1 << 1, // Favor activation for tweaking with arrows or gamepad (e.g. for Slider/Drag). Default for Space key and if keyboard is not used. ImGuiActivateFlags_TryToPreserveState = 1 << 2, // Request widget to preserve state if it can (e.g. InputText will try to preserve cursor/selection) - ImGuiActivateFlags_FromTabbing = 1 << 3, // Activation requested by a tabbing request + ImGuiActivateFlags_FromTabbing = 1 << 3, // Activation requested by a tabbing request (ImGuiNavMoveFlags_IsTabbing) ImGuiActivateFlags_FromShortcut = 1 << 4, // Activation requested by an item shortcut via SetNextItemShortcut() function. + ImGuiActivateFlags_FromFocusApi = 1 << 5, // Activation requested by an api request (ImGuiNavMoveFlags_FocusApi) }; // Early work-in-progress API for ScrollToItem() @@ -1680,6 +1719,7 @@ enum ImGuiNavRenderCursorFlags_ ImGuiNavHighlightFlags_Compact = ImGuiNavRenderCursorFlags_Compact, // Renamed in 1.91.4 ImGuiNavHighlightFlags_AlwaysDraw = ImGuiNavRenderCursorFlags_AlwaysDraw, // Renamed in 1.91.4 ImGuiNavHighlightFlags_NoRounding = ImGuiNavRenderCursorFlags_NoRounding, // Renamed in 1.91.4 + //ImGuiNavHighlightFlags_TypeThin = ImGuiNavRenderCursorFlags_Compact, // Renamed in 1.90.2 #endif }; @@ -1693,7 +1733,7 @@ enum ImGuiNavMoveFlags_ ImGuiNavMoveFlags_WrapMask_ = ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_WrapY, ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4, // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place) ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisible that only comprise elements that are already fully visible (used by PageUp/PageDown) - ImGuiNavMoveFlags_ScrollToEdgeY = 1 << 6, // Force scrolling to min/max (used by Home/End) // FIXME-NAV: Aim to remove or reword, probably unnecessary + ImGuiNavMoveFlags_ScrollToEdgeY = 1 << 6, // Force scrolling to min/max (used by Home/End) // FIXME-NAV: Aim to remove or reword as ImGuiScrollFlags ImGuiNavMoveFlags_Forwarded = 1 << 7, ImGuiNavMoveFlags_DebugNoResult = 1 << 8, // Dummy scoring for debug purpose, don't apply result ImGuiNavMoveFlags_FocusApi = 1 << 9, // Requests from focus API can land/focus/activate items even if they are marked with _NoTabStop (see NavProcessItemForTabbingRequest() for details) @@ -2050,7 +2090,8 @@ enum ImGuiDebugLogFlags_ 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_OutputToTTY = 1 << 20, // Also send output to TTY - ImGuiDebugLogFlags_OutputToTestEngine = 1 << 21, // Also send output to Test Engine + ImGuiDebugLogFlags_OutputToDebugger = 1 << 21, // Also send output to Debugger Console [Windows only] + ImGuiDebugLogFlags_OutputToTestEngine = 1 << 22, // Also send output to Dear ImGui Test Engine }; struct ImGuiDebugAllocEntry @@ -2091,26 +2132,36 @@ struct ImGuiMetricsConfig struct ImGuiStackLevelInfo { ImGuiID ID; - ImS8 QueryFrameCount; // >= 1: Query in progress - bool QuerySuccess; // Obtained result from DebugHookIdInfo() - ImGuiDataType DataType : 8; - char Desc[57]; // Arbitrarily sized buffer to hold a result (FIXME: could replace Results[] with a chunk stream?) FIXME: Now that we added CTRL+C this should be fixed. + ImS8 QueryFrameCount; // >= 1: Sub-query in progress + bool QuerySuccess; // Sub-query obtained result from DebugHookIdInfo() + ImS8 DataType; // ImGuiDataType + int DescOffset; // -1 or offset into parent's ResultsPathsBuf - ImGuiStackLevelInfo() { memset(this, 0, sizeof(*this)); } + ImGuiStackLevelInfo() { memset(this, 0, sizeof(*this)); DataType = -1; DescOffset = -1; } +}; + +struct ImGuiDebugItemPathQuery +{ + ImGuiID MainID; // ID to query details for. + bool Active; // Used to disambiguate the case when ID == 0 and e.g. some code calls PushOverrideID(0). + bool Complete; // All sub-queries are finished (some may have failed). + ImS8 Step; // -1: query stack + init Results, >= 0: filling individual stack level. + ImVector Results; + ImGuiTextBuffer ResultsDescBuf; + ImGuiTextBuffer ResultPathBuf; + + ImGuiDebugItemPathQuery() { memset(this, 0, sizeof(*this)); } }; // State for ID Stack tool queries struct ImGuiIDStackTool { + bool OptHexEncodeNonAsciiChars; + bool OptCopyToClipboardOnCtrlC; int LastActiveFrame; - int StackLevel; // -1: query stack and resize Results, >= 0: individual stack level - ImGuiID QueryId; // ID to query details for - ImVector Results; - bool CopyToClipboardOnCtrlC; float CopyToClipboardLastTime; - ImGuiTextBuffer ResultPathBuf; - ImGuiIDStackTool() { memset(this, 0, sizeof(*this)); CopyToClipboardLastTime = -FLT_MAX; } + ImGuiIDStackTool() { memset(this, 0, sizeof(*this)); LastActiveFrame = -1; OptHexEncodeNonAsciiChars = true; CopyToClipboardLastTime = -FLT_MAX; } }; //----------------------------------------------------------------------------- @@ -2138,6 +2189,14 @@ struct ImGuiContextHook struct ImGuiContext { bool Initialized; + bool WithinFrameScope; // Set by NewFrame(), cleared by EndFrame() + bool WithinFrameScopeWithImplicitWindow; // Set by NewFrame(), cleared by EndFrame() when the implicit debug window has been pushed + bool TestEngineHookItems; // Will call test engine hooks: ImGuiTestEngineHook_ItemAdd(), ImGuiTestEngineHook_ItemInfo(), ImGuiTestEngineHook_Log() + int FrameCount; + int FrameCountEnded; + int FrameCountRendered; + double Time; + char ContextName[16]; // Storage for a context name (to facilitate debugging multi-context setups) ImGuiIO IO; ImGuiPlatformIO PlatformIO; ImGuiStyle Style; @@ -2151,17 +2210,8 @@ struct ImGuiContext float FontWeight; float CurrentDpiScale; // Current window/viewport DpiScale == CurrentViewport->DpiScale ImDrawListSharedData DrawListSharedData; - double Time; - int FrameCount; - int FrameCountEnded; - int FrameCountRendered; ImGuiID WithinEndChildID; // Set within EndChild() - bool WithinFrameScope; // Set by NewFrame(), cleared by EndFrame() - bool WithinFrameScopeWithImplicitWindow; // Set by NewFrame(), cleared by EndFrame() when the implicit debug window has been pushed - bool GcCompactAll; // Request full GC - bool TestEngineHookItems; // Will call test engine hooks: ImGuiTestEngineHook_ItemAdd(), ImGuiTestEngineHook_ItemInfo(), ImGuiTestEngineHook_Log() void* TestEngine; // Test engine user data - char ContextName[16]; // Storage for a context name (to facilitate debugging multi-context setups) // Inputs ImVector InputEventsQueue; // Input events which will be trickled/written into IO structure. @@ -2192,8 +2242,8 @@ struct ImGuiContext ImVec2 WheelingAxisAvg; // Item/widgets state and tracking information - ImGuiID DebugDrawIdConflicts; // Set when we detect multiple items with the same identifier - ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by ID Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line] + ImGuiID DebugDrawIdConflictsId; // Set when we detect multiple items with the same identifier + ImGuiID DebugHookIdInfoId; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by ID Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line] ImGuiID HoveredId; // Hovered widget, filled during the frame ImGuiID HoveredIdPreviousFrame; int HoveredIdPreviousFrameItemCount; // Count numbers of items using the same ID as last frame's hovered id @@ -2212,10 +2262,11 @@ struct ImGuiContext bool ActiveIdHasBeenEditedBefore; // Was the value associated to the widget Edited over the course of the Active state. bool ActiveIdHasBeenEditedThisFrame; bool ActiveIdFromShortcut; - int ActiveIdMouseButton : 8; + ImS8 ActiveIdMouseButton; + ImGuiID ActiveIdDisabledId; // When clicking a disabled item we set ActiveId=window->MoveId to avoid interference with widget code. Actual item ID is stored here. ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior) - ImGuiWindow* ActiveIdWindow; ImGuiInputSource ActiveIdSource; // Activating source: ImGuiInputSource_Mouse OR ImGuiInputSource_Keyboard OR ImGuiInputSource_Gamepad + ImGuiWindow* ActiveIdWindow; ImGuiID ActiveIdPreviousFrame; ImGuiDeactivatedItemData DeactivatedItemData; ImGuiDataTypeStorage ActiveIdValueOnActivation; // Backup of initial value at the time of activation. ONLY SET BY SPECIFIC WIDGETS: DragXXX and SliderXXX. @@ -2245,6 +2296,7 @@ struct ImGuiContext ImGuiLastItemData LastItemData; // Storage for last submitted item (setup by ItemAdd) ImGuiNextWindowData NextWindowData; // Storage for SetNextWindow** functions bool DebugShowGroupRects; + bool GcCompactAll; // Request full GC // Shared stacks ImGuiCol DebugFlashStyleColorIdx; // (Keep close to ColorStack to share cache line) @@ -2272,18 +2324,19 @@ struct ImGuiContext ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusedWindow' ImGuiID NavFocusScopeId; // Focused focus scope (e.g. selection code often wants to "clear other items" when landing on an item of the same scope) ImGuiNavLayer NavLayer; // Focused layer (main scrolling layer, or menu/title bar layer) - ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && (IsKeyPressed(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate)) ? NavId : 0, also set when calling ActivateItem() + ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && (IsKeyPressed(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate)) ? NavId : 0, also set when calling ActivateItemByID() ImGuiID NavActivateDownId; // ~~ IsKeyDown(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyDown(ImGuiKey_NavGamepadActivate) ? NavId : 0 ImGuiID NavActivatePressedId; // ~~ IsKeyPressed(ImGuiKey_Space) || IsKeyPressed(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate) ? NavId : 0 (no repeat) ImGuiActivateFlags NavActivateFlags; ImVector NavFocusRoute; // Reversed copy focus scope stack for NavId (should contains NavFocusScopeId). This essentially follow the window->ParentWindowForFocusRoute chain. ImGuiID NavHighlightActivatedId; float NavHighlightActivatedTimer; - ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame. + ImGuiID NavNextActivateId; // Set by ActivateItemByID(), queued until next frame. ImGuiActivateFlags NavNextActivateFlags; - ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource_Keyboard or ImGuiInputSource_Mouse + ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource_Keyboard or ImGuiInputSource_Gamepad ImGuiSelectionUserData NavLastValidSelectionUserData; // Last valid data passed to SetNextItemSelectionUser(), or -1. For current window. Not reset when focusing an item that doesn't have selection data. ImS8 NavCursorHideFrames; + //ImGuiID NavActivateInputId; // Removed in 1.89.4 (July 2023). This is now part of g.NavActivateId and sets g.NavActivateFlags |= ImGuiActivateFlags_PreferInput. See commit c9a53aa74, issue #5606. // Navigation: Init & Move Requests bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest this is to perform early out in ItemAdd() @@ -2317,23 +2370,23 @@ struct ImGuiContext bool NavJustMovedToIsTabbing; // Copy of ImGuiNavMoveFlags_IsTabbing. Maybe we should store whole flags. bool NavJustMovedToHasSelectionData; // Copy of move result's ItemFlags & ImGuiItemFlags_HasSelectionUserData). Maybe we should just store ImGuiNavItemData. - // Navigation: Windowing (CTRL+TAB for list, or Menu button + keys or directional pads to move/resize) - bool ConfigNavWindowingWithGamepad; // = true. Enable CTRL+TAB by holding ImGuiKey_GamepadFaceLeft (== ImGuiKey_NavGamepadMenu). When false, the button may still be used to toggle Menu layer. + // Navigation: Windowing (Ctrl+Tab for list, or Menu button + keys or directional pads to move/resize) + bool ConfigNavWindowingWithGamepad; // = true. Enable Ctrl+Tab by holding ImGuiKey_GamepadFaceLeft (== ImGuiKey_NavGamepadMenu). When false, the button may still be used to toggle Menu layer. ImGuiKeyChord ConfigNavWindowingKeyNext; // = ImGuiMod_Ctrl | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiKey_Tab on OS X). For reconfiguration (see #4828) ImGuiKeyChord ConfigNavWindowingKeyPrev; // = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab on OS X) - ImGuiWindow* NavWindowingTarget; // Target window when doing CTRL+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most! + ImGuiWindow* NavWindowingTarget; // Target window when doing Ctrl+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most! ImGuiWindow* NavWindowingTargetAnim; // Record of last valid NavWindowingTarget until DimBgRatio and NavWindowingHighlightAlpha becomes 0.0f, so the fade-out can stay on it. - ImGuiWindow* NavWindowingListWindow; // Internal window actually listing the CTRL+Tab contents + ImGuiWindow* NavWindowingListWindow; // Internal window actually listing the Ctrl+Tab contents float NavWindowingTimer; float NavWindowingHighlightAlpha; ImGuiInputSource NavWindowingInputSource; - bool NavWindowingToggleLayer; - ImGuiKey NavWindowingToggleKey; + bool NavWindowingToggleLayer; // Set while Alt or GamepadMenu is held, may be cleared by other operations, and processed when releasing the key. + ImGuiKey NavWindowingToggleKey; // Keyboard/gamepad key used when toggling to menu layer. ImVec2 NavWindowingAccumDeltaPos; ImVec2 NavWindowingAccumDeltaSize; // Render - float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) + float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and Ctrl+Tab list) // Drag and Drop bool DragDropActive; @@ -2346,7 +2399,9 @@ struct ImGuiContext ImRect DragDropTargetRect; // Store rectangle of current target candidate (we favor small targets when overlapping) ImRect DragDropTargetClipRect; // Store ClipRect at the time of item's drawing ImGuiID DragDropTargetId; - ImGuiDragDropFlags DragDropAcceptFlags; + ImGuiID DragDropTargetFullViewport; + ImGuiDragDropFlags DragDropAcceptFlagsCurr; + ImGuiDragDropFlags DragDropAcceptFlagsPrev; float DragDropAcceptIdCurrRectSurface; // Target item surface (we resolve overlapping targets by prioritizing the smaller surface) ImGuiID DragDropAcceptIdCurr; // Target item id (set at the time of accepting the payload) ImGuiID DragDropAcceptIdPrev; // Target item id from previous frame (we need to store this to allow for overlapping drag and drop targets) @@ -2396,10 +2451,11 @@ struct ImGuiContext // Widget state ImGuiInputTextState InputTextState; + ImGuiTextIndex InputTextLineIndex; // Temporary storage ImGuiInputTextDeactivatedState InputTextDeactivatedState; ImFontBaked InputTextPasswordFontBackupBaked; ImFontFlags InputTextPasswordFontBackupFlags; - ImGuiID TempInputId; // Temporary text input when CTRL+clicking on a slider, etc. + ImGuiID TempInputId; // Temporary text input when using Ctrl+Click on a slider, etc. ImGuiDataTypeStorage DataTypeZeroValue; // 0 for all data types int BeginMenuDepth; int BeginComboDepth; @@ -2453,14 +2509,14 @@ struct ImGuiContext // Capture/Logging bool LogEnabled; // Currently capturing + bool LogLineFirstItem; ImGuiLogFlags LogFlags; // Capture flags/type ImGuiWindow* LogWindow; ImFileHandle LogFile; // If != NULL log to stdout/ file ImGuiTextBuffer LogBuffer; // Accumulation buffer when log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators. - const char* LogNextPrefix; + const char* LogNextPrefix; // See comment in LogSetNextTextDecoration(): doesn't copy underlying data, use carefully! const char* LogNextSuffix; float LogLinePosY; - bool LogLineFirstItem; int LogDepthRef; int LogDepthToExpand; int LogDepthToExpandDefault; // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call. @@ -2476,7 +2532,7 @@ struct ImGuiContext // Debug Tools // (some of the highly frequently used data are interleaved in other structures above: DebugBreakXXX fields, DebugHookIdInfo, DebugLocateId etc.) - int DebugDrawIdConflictsCount; // Locked count (preserved when holding CTRL) + int DebugDrawIdConflictsCount; // Locked count (preserved when holding Ctrl) ImGuiDebugLogFlags DebugLogFlags; ImGuiTextBuffer DebugLogBuf; ImGuiTextIndex DebugLogIndex; @@ -2493,8 +2549,13 @@ struct ImGuiContext float DebugFlashStyleColorTime; ImVec4 DebugFlashStyleColorBackup; ImGuiMetricsConfig DebugMetricsConfig; + ImGuiDebugItemPathQuery DebugItemPathQuery; ImGuiIDStackTool DebugIDStackTool; ImGuiDebugAllocInfo DebugAllocInfo; +#if defined(IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS) && !defined(IMGUI_DISABLE_DEBUG_TOOLS) + ImGuiStorage DebugDrawIdConflictsAliveCount; + ImGuiStorage DebugDrawIdConflictsHighlightSet; +#endif // Misc float FramerateSecPerFrame[60]; // Calculate estimate of framerate for user over the last 60 frames.. @@ -2503,17 +2564,20 @@ struct ImGuiContext float FramerateSecPerFrameAccum; int WantCaptureMouseNextFrame; // Explicit capture override via SetNextFrameWantCaptureMouse()/SetNextFrameWantCaptureKeyboard(). Default to -1. int WantCaptureKeyboardNextFrame; // " - int WantTextInputNextFrame; // Copied in EndFrame() from g.PlatformImeData.WanttextInput. Needs to be set for some backends (SDL3) to emit character inputs. + int WantTextInputNextFrame; // Copied in EndFrame() from g.PlatformImeData.WantTextInput. Needs to be set for some backends (SDL3) to emit character inputs. ImVector TempBuffer; // Temporary text buffer char TempKeychordName[64]; ImGuiContext(ImFontAtlas* shared_font_atlas); + ~ImGuiContext(); }; //----------------------------------------------------------------------------- // [SECTION] ImGuiWindowTempData, ImGuiWindow //----------------------------------------------------------------------------- +#define IMGUI_WINDOW_HARD_MIN_SIZE 4.0f + // Transient per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the DC variable name in ImGuiWindow. // (That's theory, in practice the delimitation between ImGuiWindow and ImGuiWindowTempData is quite tenuous and could be reconsidered..) // (This doesn't need a constructor because we zero-clear it as part of ImGuiWindow and all frame-temporary data are setup on Begin) @@ -2625,13 +2689,14 @@ struct IMGUI_API ImGuiWindow short BeginOrderWithinParent; // Begin() order within immediate parent window, if we are a child window. Otherwise 0. short BeginOrderWithinContext; // Begin() order within entire imgui context. This is mostly used for debugging submission order related issues. short FocusOrder; // Order within WindowsFocusOrder[], altered when windows are focused. + ImGuiDir AutoPosLastDirection; ImS8 AutoFitFramesX, AutoFitFramesY; bool AutoFitOnlyGrows; - ImGuiDir AutoPosLastDirection; ImS8 HiddenFramesCanSkipItems; // Hide the window for N frames ImS8 HiddenFramesCannotSkipItems; // Hide the window for N frames while allowing items to be submitted so we can measure their size ImS8 HiddenFramesForRenderOnly; // Hide the window until frame N at Render() time only ImS8 DisableInputsFrames; // Disable window interactions for N frames + ImGuiWindowBgClickFlags BgClickFlags : 8; // Configure behavior of click+dragging on window bg/void or over items. Default sets by io.ConfigWindowsMoveFromTitleBarOnly. If you use this please report in #3379. ImGuiCond SetWindowPosAllowFlags : 8; // store acceptable condition flags for SetNextWindowPos() use. ImGuiCond SetWindowSizeAllowFlags : 8; // store acceptable condition flags for SetNextWindowSize() use. ImGuiCond SetWindowCollapsedAllowFlags : 8; // store acceptable condition flags for SetNextWindowCollapsed() use. @@ -2671,7 +2736,7 @@ struct IMGUI_API ImGuiWindow ImGuiWindow* RootWindowPopupTree; // Point to ourself or first ancestor that is not a child window. Cross through popups parent<>child. ImGuiWindow* RootWindowForTitleBarHighlight; // Point to ourself or first ancestor which will display TitleBgActive color when this window is active. ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag. - ImGuiWindow* ParentWindowForFocusRoute; // Set to manual link a window to its logical parent so that Shortcut() chain are honoerd (e.g. Tool linked to Document) + ImGuiWindow* ParentWindowForFocusRoute; // Set to manual link a window to its logical parent so that Shortcut() chain are honored (e.g. Tool linked to Document) ImGuiWindow* NavLastChildNavWindow; // When going to the menu bar, we remember the child window we came from. (This could probably be made implicit if we kept g.Windows sorted by last focused including child window.) ImGuiID NavLastIds[ImGuiNavLayer_COUNT]; // Last known NavId for this window, per layer (0/1) @@ -2698,7 +2763,7 @@ public: ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight)); } ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight; return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight); } - // [Obsolete] ImGuiWindow::CalcFontSize() was removed in 1.92.x because error-prone/misleading. You can use window->FontRefSize for a copy of g.FontSize at the time of the last Begin() call for this window. + // [OBSOLETE] ImGuiWindow::CalcFontSize() was removed in 1.92.0 because error-prone/misleading. You can use window->FontRefSize for a copy of g.FontSize at the time of the last Begin() call for this window. //float CalcFontSize() const { ImGuiContext& g = *Ctx; return g.FontSizeBase * FontWindowScale * FontWindowScaleParents; }; @@ -2733,7 +2798,7 @@ struct ImGuiTabItem int LastFrameSelected; // This allows us to infer an ordered list of the last activated tabs with little maintenance float Offset; // Position relative to beginning of tab float Width; // Width currently displayed - float ContentWidth; // Width of label, stored during BeginTabItem() call + float ContentWidth; // Width of label + padding, stored during BeginTabItem() call (misnamed as "Content" would normally imply width of label only) float RequestedWidth; // Width optionally requested by caller, -1.0f is unused ImS32 NameOffset; // When Window==NULL, offset to name within parent ImGuiTabBar::TabsNames ImS16 BeginOrder; // BeginTabItem() order, used to re-order tabs after toggling ImGuiTabBarFlags_Reorderable @@ -2752,10 +2817,11 @@ struct IMGUI_API ImGuiTabBar ImGuiID ID; // Zero for tab-bars used by docking ImGuiID SelectedTabId; // Selected tab/window ImGuiID NextSelectedTabId; // Next selected tab/window. Will also trigger a scrolling animation - ImGuiID VisibleTabId; // Can occasionally be != SelectedTabId (e.g. when previewing contents for CTRL+TAB preview) + ImGuiID VisibleTabId; // Can occasionally be != SelectedTabId (e.g. when previewing contents for Ctrl+Tab preview) int CurrFrameVisible; int PrevFrameVisible; ImRect BarRect; + float BarRectPrevWidth; // Backup of previous width. When width change we enforce keep horizontal scroll on focused tab. float CurrTabsContentsHeight; float PrevTabsContentsHeight; // Record the height of contents submitted below the tab bar float WidthAllTabs; // Actual width of all tabs (locked during layout) @@ -2774,6 +2840,7 @@ struct IMGUI_API ImGuiTabBar bool WantLayout; bool VisibleTabWasSubmitted; bool TabsAddedNew; // Set to true when a new tab item or button has been added to the tab bar during last frame + bool ScrollButtonEnabled; ImS16 TabsActiveCount; // Number of tabs submitted this frame. ImS16 LastTabItemIdx; // Index of last BeginTabItem() tab for use by EndTabItem() float ItemSpacingY; @@ -2988,9 +3055,9 @@ struct IMGUI_API ImGuiTable 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). - bool DisableDefaultContextMenu; // Disable default context menu contents. You may submit your own using TableBeginContextMenuPopup()/EndPopup() + bool DisableDefaultContextMenu; // Disable default context menu. You may submit your own using TableBeginContextMenuPopup()/EndPopup() bool IsSettingsRequestLoad; - bool IsSettingsDirty; // Set when table settings have changed and needs to be reported into ImGuiTableSetttings data. + 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 IsResetDisplayOrderRequest; @@ -3014,6 +3081,7 @@ struct IMGUI_API ImGuiTable // sizeof() ~ 136 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() @@ -3094,6 +3162,7 @@ namespace ImGui IMGUI_API void UpdateWindowSkipRefresh(ImGuiWindow* window); IMGUI_API ImVec2 CalcWindowNextAutoFitSize(ImGuiWindow* window); IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy); + IMGUI_API bool IsWindowInBeginStack(ImGuiWindow* window); IMGUI_API bool IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent); IMGUI_API bool IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below); IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window); @@ -3122,7 +3191,7 @@ namespace ImGui IMGUI_API void SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags); // Fonts, drawing - IMGUI_API void RegisterUserTexture(ImTextureData* tex); // Register external texture + IMGUI_API void RegisterUserTexture(ImTextureData* tex); // Register external texture. EXPERIMENTAL: DO NOT USE YET. IMGUI_API void UnregisterUserTexture(ImTextureData* tex); IMGUI_API void RegisterFontAtlas(ImFontAtlas* atlas); IMGUI_API void UnregisterFontAtlas(ImFontAtlas* atlas); @@ -3143,19 +3212,21 @@ namespace ImGui IMGUI_API void Initialize(); IMGUI_API void Shutdown(); // Since 1.60 this is a _private_ function. You can call DestroyContext() to destroy the context created by CreateContext(). + // Context name & generic context hooks + IMGUI_API void SetContextName(ImGuiContext* ctx, const char* name); + IMGUI_API ImGuiID AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook); + IMGUI_API void RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_to_remove); + IMGUI_API void CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType type); + // NewFrame IMGUI_API void UpdateInputEvents(bool trickle_fast_inputs); IMGUI_API void UpdateHoveredWindowAndCaptureFlags(const ImVec2& mouse_pos); IMGUI_API void FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window); IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window); + IMGUI_API void StopMouseMovingWindow(); IMGUI_API void UpdateMouseMovingWindowNewFrame(); IMGUI_API void UpdateMouseMovingWindowEndFrame(); - // Generic context hooks - IMGUI_API ImGuiID AddContextHook(ImGuiContext* context, const ImGuiContextHook* hook); - IMGUI_API void RemoveContextHook(ImGuiContext* context, ImGuiID hook_to_remove); - IMGUI_API void CallContextHooks(ImGuiContext* context, ImGuiContextHookType type); - // Viewports IMGUI_API void ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale); IMGUI_API void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport); @@ -3194,7 +3265,6 @@ namespace ImGui // Basic Accessors inline ImGuiItemStatusFlags GetItemStatusFlags() { ImGuiContext& g = *GImGui; return g.LastItemData.StatusFlags; } - inline ImGuiItemFlags GetItemFlags() { ImGuiContext& g = *GImGui; return g.LastItemData.ItemFlags; } inline ImGuiID GetActiveID() { ImGuiContext& g = *GImGui; return g.ActiveId; } inline ImGuiID GetFocusID() { ImGuiContext& g = *GImGui; return g.NavId; } IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window); @@ -3219,7 +3289,8 @@ namespace ImGui IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h); IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); IMGUI_API void PushMultiItemsWidths(int components, float width_full); - IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess); + IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess, float width_min); + IMGUI_API void CalcClipRectVisibleItemsY(const ImRect& clip_rect, const ImVec2& pos, float items_height, int* out_visible_start, int* out_visible_end); // Parameter stacks (shared) IMGUI_API const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx); @@ -3287,7 +3358,7 @@ namespace ImGui // This should be part of a larger set of API: FocusItem(offset = -1), FocusItemByID(id), ActivateItem(offset = -1), ActivateItemByID(id) etc. which are // much harder to design and implement than expected. I have a couple of private branches on this matter but it's not simple. For now implementing the easy ones. IMGUI_API void FocusItem(); // Focus last item (no selection/activation). - IMGUI_API void ActivateItemByID(ImGuiID id); // Activate an item by ID (button, checkbox, tree node etc.). Activation is queued and processed on the next frame when the item is encountered again. + IMGUI_API void ActivateItemByID(ImGuiID id); // Activate an item by ID (button, checkbox, tree node etc.). Activation is queued and processed on the next frame when the item is encountered again. Was called 'ActivateItem()' before 1.89.7. // Inputs // FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions. @@ -3347,7 +3418,7 @@ namespace ImGui // Legacy functions use ImGuiKeyOwner_Any meaning that they typically ignore ownership, unless a call to SetKeyOwner() explicitly used ImGuiInputFlags_LockThisFrame or ImGuiInputFlags_LockUntilRelease. // - Binding generators may want to ignore those for now, or suffix them with Ex() until we decide if this gets moved into public API. IMGUI_API bool IsKeyDown(ImGuiKey key, ImGuiID owner_id); - IMGUI_API bool IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id = 0); // Important: when transitioning from old to new IsKeyPressed(): old API has "bool repeat = true", so would default to repeat. New API requiress explicit ImGuiInputFlags_Repeat. + IMGUI_API bool IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id = 0); // Important: when transitioning from old to new IsKeyPressed(): old API has "bool repeat = true", so would default to repeat. New API requires explicit ImGuiInputFlags_Repeat. IMGUI_API bool IsKeyReleased(ImGuiKey key, ImGuiID owner_id); IMGUI_API bool IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id = 0); IMGUI_API bool IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id); @@ -3389,9 +3460,11 @@ namespace ImGui // Drag and Drop IMGUI_API bool IsDragDropActive(); IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id); + IMGUI_API bool BeginDragDropTargetViewport(ImGuiViewport* viewport, const ImRect* p_bb = NULL); IMGUI_API void ClearDragDrop(); IMGUI_API bool IsDragDropPayloadBeingAccepted(); - IMGUI_API void RenderDragDropTargetRect(const ImRect& bb, const ImRect& item_clip_rect); + IMGUI_API void RenderDragDropTargetRectForItem(const ImRect& bb); + IMGUI_API void RenderDragDropTargetRectEx(ImDrawList* draw_list, const ImRect& bb); // Typing-Select API // (provide Windows Explorer style "select items by typing partial name" + "cycle through items by typing same letter" feature) @@ -3454,6 +3527,7 @@ namespace ImGui 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); @@ -3485,6 +3559,8 @@ namespace ImGui // Tab Bars inline ImGuiTabBar* GetCurrentTabBar() { ImGuiContext& g = *GImGui; return g.CurrentTabBar; } + IMGUI_API ImGuiTabBar* TabBarFindByID(ImGuiID id); + IMGUI_API void TabBarRemove(ImGuiTabBar* tab_bar); IMGUI_API bool BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& bb, ImGuiTabBarFlags flags); IMGUI_API ImGuiTabItem* TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id); IMGUI_API ImGuiTabItem* TabBarFindTabByOrder(ImGuiTabBar* tab_bar, int order); @@ -3515,6 +3591,7 @@ namespace ImGui IMGUI_API void RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known); IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders = true, float rounding = 0.0f); 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 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS @@ -3528,7 +3605,7 @@ namespace ImGui IMGUI_API void RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col); IMGUI_API void RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz); IMGUI_API void RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col); - IMGUI_API void RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding); + IMGUI_API void RenderRectFilledInRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float fill_x0, float fill_x1, float rounding); IMGUI_API void RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer, const ImRect& inner, ImU32 col, float rounding); // Widgets: Text @@ -3594,7 +3671,7 @@ namespace ImGui IMGUI_API void InputTextDeactivateHook(ImGuiID id); IMGUI_API bool TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags); IMGUI_API bool TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min = NULL, const void* p_clamp_max = NULL); - 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.ActiveId == id && g.TempInputId == 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. @@ -3603,6 +3680,7 @@ namespace ImGui IMGUI_API void ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags); IMGUI_API void ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags); IMGUI_API void ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags); + inline void SetNextItemColorMarker(ImU32 col) { ImGuiContext& g = *GImGui; g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasColorMarker; g.NextItemData.ColorMarker = col; } // Plot IMGUI_API int PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, const ImVec2& size_arg); @@ -3640,6 +3718,7 @@ namespace ImGui IMGUI_API bool DebugBreakButton(const char* label, const char* description_of_location); IMGUI_API void DebugBreakButtonTooltip(bool keyboard_only, const char* description_of_location); IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); + IMGUI_API ImU64 DebugTextureIDToU64(ImTextureID tex_id); IMGUI_API void DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end); IMGUI_API void DebugNodeColumns(ImGuiOldColumns* columns); IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label); @@ -3697,7 +3776,7 @@ struct ImFontLoader bool (*FontSrcContainsGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint); bool (*FontBakedInit)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src); void (*FontBakedDestroy)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src); - bool (*FontBakedLoadGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph); + bool (*FontBakedLoadGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x); // Size of backend data, Per Baked * Per Source. Buffers are managed by core to avoid excessive allocations. // FIXME: At this point the two other types of buffers may be managed by core to be consistent? @@ -3717,18 +3796,21 @@ typedef ImFontLoader ImFontBuilderIO; // [renamed/changed in 1.92] The types are // [SECTION] ImFontAtlas internal API //----------------------------------------------------------------------------- +#define IMGUI_FONT_SIZE_MAX (512.0f) +#define IMGUI_FONT_SIZE_THRESHOLD_FOR_LOADADVANCEXONLYMODE (128.0f) + // Helpers: ImTextureRef ==/!= operators provided as convenience // (note that _TexID and _TexData are never set simultaneously) inline bool operator==(const ImTextureRef& lhs, const ImTextureRef& rhs) { return lhs._TexID == rhs._TexID && lhs._TexData == rhs._TexData; } inline bool operator!=(const ImTextureRef& lhs, const ImTextureRef& rhs) { return lhs._TexID != rhs._TexID || lhs._TexData != rhs._TexData; } // Refer to ImFontAtlasPackGetRect() to better understand how this works. -#define ImFontAtlasRectId_IndexMask_ (0x000FFFFF) // 20-bits: index to access builder->RectsIndex[]. +#define ImFontAtlasRectId_IndexMask_ (0x0007FFFF) // 20-bits signed: index to access builder->RectsIndex[]. #define ImFontAtlasRectId_GenerationMask_ (0x3FF00000) // 10-bits: entry generation, so each ID is unique and get can safely detected old identifiers. #define ImFontAtlasRectId_GenerationShift_ (20) -inline int ImFontAtlasRectId_GetIndex(ImFontAtlasRectId id) { return id & ImFontAtlasRectId_IndexMask_; } -inline int ImFontAtlasRectId_GetGeneration(ImFontAtlasRectId id) { return (id & ImFontAtlasRectId_GenerationMask_) >> ImFontAtlasRectId_GenerationShift_; } -inline ImFontAtlasRectId ImFontAtlasRectId_Make(int index_idx, int gen_idx) { IM_ASSERT(index_idx < ImFontAtlasRectId_IndexMask_ && gen_idx < (ImFontAtlasRectId_GenerationMask_ >> ImFontAtlasRectId_GenerationShift_)); return (ImFontAtlasRectId)(index_idx | (gen_idx << ImFontAtlasRectId_GenerationShift_)); } +inline int ImFontAtlasRectId_GetIndex(ImFontAtlasRectId id) { return (id & ImFontAtlasRectId_IndexMask_); } +inline unsigned int ImFontAtlasRectId_GetGeneration(ImFontAtlasRectId id) { return (unsigned int)(id & ImFontAtlasRectId_GenerationMask_) >> ImFontAtlasRectId_GenerationShift_; } +inline ImFontAtlasRectId ImFontAtlasRectId_Make(int index_idx, int gen_idx) { IM_ASSERT(index_idx >= 0 && index_idx <= ImFontAtlasRectId_IndexMask_ && gen_idx <= (ImFontAtlasRectId_GenerationMask_ >> ImFontAtlasRectId_GenerationShift_)); return (ImFontAtlasRectId)(index_idx | (gen_idx << ImFontAtlasRectId_GenerationShift_)); } // Packed rectangle lookup entry (we need an indirection to allow removing/reordering rectangles) // User are returned ImFontAtlasRectId values which are meant to be persistent. @@ -3738,7 +3820,7 @@ inline ImFontAtlasRectId ImFontAtlasRectId_Make(int index_idx, int gen_idx) struct ImFontAtlasRectEntry { int TargetIndex : 20; // When Used: ImFontAtlasRectId -> into Rects[]. When unused: index to next unused RectsIndex[] slot to consume free-list. - int Generation : 10; // Increased each time the entry is reused for a new rectangle. + unsigned int Generation : 10; // Increased each time the entry is reused for a new rectangle. unsigned int IsUsed : 1; }; @@ -3804,6 +3886,7 @@ IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildDestroy(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildMain(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildSetupFontLoader(ImFontAtlas* atlas, const ImFontLoader* font_loader); +IMGUI_API void ImFontAtlasBuildNotifySetFont(ImFontAtlas* atlas, ImFont* old_font, ImFont* new_font); IMGUI_API void ImFontAtlasBuildUpdatePointers(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildRenderBitmapFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char); IMGUI_API void ImFontAtlasBuildClear(ImFontAtlas* atlas); // Clear output and custom rects @@ -3825,6 +3908,7 @@ IMGUI_API void ImFontAtlasFontSourceAddToFont(ImFontAtlas* atlas, I IMGUI_API void ImFontAtlasFontDestroySourceData(ImFontAtlas* atlas, ImFontConfig* src); IMGUI_API bool ImFontAtlasFontInitOutput(ImFontAtlas* atlas, ImFont* font); // Using FontDestroyOutput/FontInitOutput sequence useful notably if font loader params have changed IMGUI_API void ImFontAtlasFontDestroyOutput(ImFontAtlas* atlas, ImFont* font); +IMGUI_API void ImFontAtlasFontRebuildOutput(ImFontAtlas* atlas, ImFont* font); IMGUI_API void ImFontAtlasFontDiscardBakes(ImFontAtlas* atlas, ImFont* font, int unused_frames); IMGUI_API ImGuiID ImFontAtlasBakedGetId(ImGuiID font_id, float baked_size, float baked_weight, float rasterizer_density); @@ -3833,6 +3917,7 @@ IMGUI_API ImFontBaked* ImFontAtlasBakedGetClosestMatch(ImFontAtlas* atlas, IMGUI_API ImFontBaked* ImFontAtlasBakedAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_weight, float font_rasterizer_density, ImGuiID baked_id); IMGUI_API void ImFontAtlasBakedDiscard(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked); IMGUI_API ImFontGlyph* ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, const ImFontGlyph* in_glyph); +IMGUI_API void ImFontAtlasBakedAddFontGlyphAdvancedX(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImWchar codepoint, float advance_x); IMGUI_API void ImFontAtlasBakedDiscardFontGlyph(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked, ImFontGlyph* glyph); IMGUI_API void ImFontAtlasBakedSetFontGlyphBitmap(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImFontGlyph* glyph, ImTextureRect* r, const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch); @@ -3880,7 +3965,7 @@ extern const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiI #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register item label and status flags (optional) #define IMGUI_TEST_ENGINE_LOG(_FMT,...) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log #else -#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) ((void)0) +#define IMGUI_TEST_ENGINE_ITEM_ADD(_ID,_BB,_ITEM_DATA) ((void)0) #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) ((void)g) #endif diff --git a/dep/imgui/include/imgui_stdlib.h b/dep/imgui/include/imgui_stdlib.h index 697fc34ad..cf4b528bd 100644 --- a/dep/imgui/include/imgui_stdlib.h +++ b/dep/imgui/include/imgui_stdlib.h @@ -1,9 +1,21 @@ // dear imgui: wrappers for C++ standard library (STL) types (std::string, etc.) + // This is also an example of how you may wrap your own similar types. +// TL;DR; this is using the ImGuiInputTextFlags_CallbackResize facility, +// which also demonstrated in 'Dear ImGui Demo->Widgets->Text Input->Resize Callback'. // Changelog: // - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string +// Usage: +// { +// #include "misc/cpp/imgui_stdlib.h" +// #include "misc/cpp/imgui_stdlib.cpp" // <-- If you want to include implementation without messing with your project/build. +// [...] +// std::string my_string; +// ImGui::InputText("my string", &my_string); +// } + // See more C++ related extension (fmt, RAII, syntaxis sugar) on Wiki: // https://github.com/ocornut/imgui/wiki/Useful-Extensions#cness diff --git a/dep/imgui/include/imstb_textedit.h b/dep/imgui/include/imstb_textedit.h index 33eef7095..583508f0b 100644 --- a/dep/imgui/include/imstb_textedit.h +++ b/dep/imgui/include/imstb_textedit.h @@ -5,7 +5,8 @@ // - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783) // - Added name to struct or it may be forward declared in our code. // - Added UTF-8 support (see https://github.com/nothings/stb/issues/188 + https://github.com/ocornut/imgui/pull/7925) -// Grep for [DEAR IMGUI] to find the changes. +// - Changed STB_TEXTEDIT_INSERTCHARS() to return inserted count (instead of 0/1 bool), allowing partial insertion. +// Grep for [DEAR IMGUI] to find some changes. // - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_* // stb_textedit.h - v1.14 - public domain - Sean Barrett @@ -39,6 +40,7 @@ // // VERSION HISTORY // +// !!!! (2025-10-23) changed STB_TEXTEDIT_INSERTCHARS() to return inserted count (instead of 0/1 bool), allowing partial insertion. // 1.14 (2021-07-11) page up/down, various fixes // 1.13 (2019-02-07) fix bug in undo size management // 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash @@ -147,7 +149,8 @@ // as manually wordwrapping for end-of-line positioning // // STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i -// STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*) +// STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) try to insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*) +// returns number of characters actually inserted. [DEAR IMGUI] // // STB_TEXTEDIT_K_SHIFT a power of two that is or'd in to a keyboard input to represent the shift key // @@ -181,10 +184,10 @@ // // To support UTF-8: // -// STB_TEXTEDIT_GETPREVCHARINDEX returns index of previous character -// STB_TEXTEDIT_GETNEXTCHARINDEX returns index of next character +// STB_TEXTEDIT_GETPREVCHARINDEX returns index of previous character +// STB_TEXTEDIT_GETNEXTCHARINDEX returns index of next character // Do NOT define STB_TEXTEDIT_KEYTOTEXT. -// Instead, call stb_textedit_text() directly for text contents. +// Instead, call stb_textedit_text() directly for text contents. // // Keyboard input must be encoded as a single integer value; e.g. a character code // and some bitflags that represent shift states. to simplify the interface, SHIFT must @@ -260,7 +263,7 @@ // // text: (added 2025) // call this to directly send text input the textfield, which is required -// for UTF-8 support, because stb_textedit_key() + STB_TEXTEDIT_KEYTOTEXT() +// for UTF-8 support, because stb_textedit_key() + STB_TEXTEDIT_KEYTOTEXT() // cannot infer text length. // // @@ -427,7 +430,7 @@ typedef struct // // traverse the layout to locate the nearest character to a display position -static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y) +static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y, int* out_side_on_line) { StbTexteditRow r; int n = STB_TEXTEDIT_STRINGLEN(str); @@ -437,6 +440,7 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y) r.x0 = r.x1 = 0; r.ymin = r.ymax = 0; r.num_chars = 0; + *out_side_on_line = 0; // search rows to find one that straddles 'y' while (i < n) { @@ -456,7 +460,10 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y) // below all text, return 'after' last character if (i >= n) + { + *out_side_on_line = 1; return n; + } // check if it's before the beginning of the line if (x < r.x0) @@ -469,6 +476,7 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y) for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) { float w = STB_TEXTEDIT_GETWIDTH(str, i, k); if (x < prev_x+w) { + *out_side_on_line = (k == 0) ? 0 : 1; if (x < prev_x+w/2) return k+i; else @@ -480,6 +488,7 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y) } // if the last character is a newline, return that. otherwise return 'after' the last character + *out_side_on_line = 1; if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE) return i+r.num_chars-1; else @@ -491,6 +500,7 @@ static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *st { // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse // goes off the top or bottom of the text + int side_on_line; if( state->single_line ) { StbTexteditRow r; @@ -498,16 +508,18 @@ static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *st y = r.ymin; } - state->cursor = stb_text_locate_coord(str, x, y); + state->cursor = stb_text_locate_coord(str, x, y, &side_on_line); state->select_start = state->cursor; state->select_end = state->cursor; state->has_preferred_x = 0; + str->LastMoveDirectionLR = (ImS8)(side_on_line ? ImGuiDir_Right : ImGuiDir_Left); } // API drag: on mouse drag, move the cursor and selection endpoint to the clicked location static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) { int p = 0; + int side_on_line; // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse // goes off the top or bottom of the text @@ -521,8 +533,9 @@ static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *sta if (state->select_start == state->select_end) state->select_start = state->cursor; - p = stb_text_locate_coord(str, x, y); + p = stb_text_locate_coord(str, x, y, &side_on_line); state->cursor = state->select_end = p; + str->LastMoveDirectionLR = (ImS8)(side_on_line ? ImGuiDir_Right : ImGuiDir_Left); } ///////////////////////////////////////////////////////////////////////////// @@ -572,6 +585,8 @@ static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING STB_TEXTEDIT_LAYOUTROW(&r, str, i); if (n < i + r.num_chars) break; + if (str->LastMoveDirectionLR == ImGuiDir_Right && str->Stb->cursor > 0 && str->Stb->cursor == i + r.num_chars && STB_TEXTEDIT_GETCHAR(str, i + r.num_chars - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] Wrapping point handling + break; if (i + r.num_chars == z && z > 0 && STB_TEXTEDIT_GETCHAR(str, z - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] special handling for last line break; // [DEAR IMGUI] prev_start = i; @@ -668,6 +683,35 @@ static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditSt } } +// [DEAR IMGUI] Extracted this function so we can more easily add support for word-wrapping. +#ifndef STB_TEXTEDIT_MOVELINESTART +static int stb_textedit_move_line_start(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor) +{ + if (state->single_line) + return 0; + while (cursor > 0) { + int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, cursor); + if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE) + break; + cursor = prev; + } + return cursor; +} +#define STB_TEXTEDIT_MOVELINESTART stb_textedit_move_line_start +#endif +#ifndef STB_TEXTEDIT_MOVELINEEND +static int stb_textedit_move_line_end(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor) +{ + int n = STB_TEXTEDIT_STRINGLEN(str); + if (state->single_line) + return n; + while (cursor < n && STB_TEXTEDIT_GETCHAR(str, cursor) != STB_TEXTEDIT_NEWLINE) + cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, cursor); + return cursor; +} +#define STB_TEXTEDIT_MOVELINEEND stb_textedit_move_line_end +#endif + #ifdef STB_TEXTEDIT_IS_SPACE static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx ) { @@ -734,7 +778,8 @@ static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditS stb_textedit_clamp(str, state); stb_textedit_delete_selection(str,state); // try to insert the characters - if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len)) { + len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len); + if (len) { stb_text_makeundo_insert(state, state->cursor, len); state->cursor += len; state->has_preferred_x = 0; @@ -759,13 +804,15 @@ static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* sta if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) { stb_text_makeundo_replace(str, state, state->cursor, 1, 1); STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1); - if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) { + text_len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len); + if (text_len) { state->cursor += text_len; state->has_preferred_x = 0; } } else { stb_textedit_delete_selection(str, state); // implicitly clamps - if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) { + text_len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len); + if (text_len) { stb_text_makeundo_insert(state, state->cursor, text_len); state->cursor += text_len; state->has_preferred_x = 0; @@ -921,8 +968,8 @@ retry: // [DEAR IMGUI] // going down while being on the last line shouldn't bring us to that line end - if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE) - break; + //if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE) + // break; // now find character position down a row state->cursor = start; @@ -943,6 +990,8 @@ retry: } stb_textedit_clamp(str, state); + if (state->cursor == find.first_char + find.length) + str->LastMoveDirectionLR = ImGuiDir_Left; state->has_preferred_x = 1; state->preferred_x = goal_x; @@ -1007,6 +1056,10 @@ retry: } stb_textedit_clamp(str, state); + if (state->cursor == find.first_char) + str->LastMoveDirectionLR = ImGuiDir_Right; + else if (state->cursor == find.prev_first) + str->LastMoveDirectionLR = ImGuiDir_Left; state->has_preferred_x = 1; state->preferred_x = goal_x; @@ -1024,7 +1077,7 @@ retry: prev_scan = prev; } find.first_char = find.prev_first; - find.prev_first = prev_scan; + find.prev_first = STB_TEXTEDIT_MOVELINESTART(str, state, prev_scan); } break; } @@ -1098,10 +1151,7 @@ retry: case STB_TEXTEDIT_K_LINESTART: stb_textedit_clamp(str, state); stb_textedit_move_to_first(state); - if (state->single_line) - state->cursor = 0; - else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE) - state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor); + state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor); state->has_preferred_x = 0; break; @@ -1109,13 +1159,9 @@ retry: case STB_TEXTEDIT_K_LINEEND2: #endif case STB_TEXTEDIT_K_LINEEND: { - int n = STB_TEXTEDIT_STRINGLEN(str); stb_textedit_clamp(str, state); - stb_textedit_move_to_first(state); - if (state->single_line) - state->cursor = n; - else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE) - state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor); + stb_textedit_move_to_last(str, state); + state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor); state->has_preferred_x = 0; break; } @@ -1126,10 +1172,7 @@ retry: case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT: stb_textedit_clamp(str, state); stb_textedit_prep_selection_at_cursor(state); - if (state->single_line) - state->cursor = 0; - else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE) - state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor); + state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor); state->select_end = state->cursor; state->has_preferred_x = 0; break; @@ -1138,13 +1181,9 @@ retry: case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT: #endif case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: { - int n = STB_TEXTEDIT_STRINGLEN(str); stb_textedit_clamp(str, state); stb_textedit_prep_selection_at_cursor(state); - if (state->single_line) - state->cursor = n; - else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE) - state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor); + state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor); state->select_end = state->cursor; state->has_preferred_x = 0; break; @@ -1319,7 +1358,7 @@ static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) // check type of recorded action: if (u.insert_length) { // easy case: was a deletion, so we need to insert n characters - STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length); + u.insert_length = STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length); s->undo_char_point -= u.insert_length; } @@ -1370,7 +1409,7 @@ static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) if (r.insert_length) { // easy case: need to insert n characters - STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length); + r.insert_length = STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length); s->redo_char_point += r.insert_length; } diff --git a/dep/imgui/src/imgui.cpp b/dep/imgui/src/imgui.cpp index c51fd5f1b..59fc8815c 100644 --- a/dep/imgui/src/imgui.cpp +++ b/dep/imgui/src/imgui.cpp @@ -1,32 +1,33 @@ -// dear imgui, v1.92.0 +// dear imgui, v1.92.6 WIP // (main code and documentation) // Help: -// - See links below. // - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that. // - Read top of imgui.cpp for more details, links and comments. +// - Add '#define IMGUI_DEFINE_MATH_OPERATORS' before including imgui.h (or in imconfig.h) to access courtesy maths operators for ImVec2 and ImVec4. // Resources: // - FAQ ........................ https://dearimgui.com/faq (in repository as docs/FAQ.md) // - Homepage ................... https://github.com/ocornut/imgui -// - Releases & changelog ....... https://github.com/ocornut/imgui/releases +// - Releases & Changelog ....... https://github.com/ocornut/imgui/releases // - Gallery .................... https://github.com/ocornut/imgui/issues?q=label%3Agallery (please post your screenshots/video there!) // - Wiki ....................... https://github.com/ocornut/imgui/wiki (lots of good stuff there) // - Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started (how to integrate in an existing app by adding ~25 lines of code) // - Third-party Extensions https://github.com/ocornut/imgui/wiki/Useful-Extensions (ImPlot & many more) -// - Bindings/Backends https://github.com/ocornut/imgui/wiki/Bindings (language bindings, backends for various tech/engines) -// - Glossary https://github.com/ocornut/imgui/wiki/Glossary +// - Bindings/Backends https://github.com/ocornut/imgui/wiki/Bindings (language bindings + backends for various tech/engines) // - Debug Tools https://github.com/ocornut/imgui/wiki/Debug-Tools +// - Glossary https://github.com/ocornut/imgui/wiki/Glossary // - Software using Dear ImGui https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui // - Issues & support ........... https://github.com/ocornut/imgui/issues // - Test Engine & Automation ... https://github.com/ocornut/imgui_test_engine (test suite, test engine to automate your apps) +// - Web version of the Demo .... https://pthom.github.io/imgui_manual_online/manual/imgui_manual.html (w/ source code browser) -// For first-time users having issues compiling/linking/running: +// For FIRST-TIME users having issues compiling/linking/running: // please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above. // Everything else should be asked in 'Issues'! We are building a database of cross-linked knowledge there. -// Since 1.92, we encourage font loading question to also be posted in 'Issues'. +// Since 1.92, we encourage font loading questions to also be posted in 'Issues'. -// Copyright (c) 2014-2025 Omar Cornut +// Copyright (c) 2014-2026 Omar Cornut // Developed by Omar Cornut and every direct or indirect contributors to the GitHub. // See LICENSE.txt for copyright and licensing details (standard MIT License). // This library is free but needs your support to sustain development and maintenance. @@ -53,7 +54,7 @@ DOCUMENTATION - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE - HOW A SIMPLE APPLICATION MAY LOOK LIKE - - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE + - USING CUSTOM BACKEND / CUSTOM ENGINE - API BREAKING CHANGES (read me when you update!) - FREQUENTLY ASKED QUESTIONS (FAQ) - Read all answers online: https://www.dearimgui.com/faq, or in docs/FAQ.md (with a Markdown viewer) @@ -78,7 +79,7 @@ CODE // [SECTION] RENDER HELPERS // [SECTION] INITIALIZATION, SHUTDOWN // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) -// [SECTION] FONTS +// [SECTION] FONTS, TEXTURES // [SECTION] ID STACK // [SECTION] INPUTS // [SECTION] ERROR CHECKING, STATE RECOVERY @@ -122,7 +123,7 @@ CODE Designed primarily for developers and content-creators, not the typical end-user! Some of the current weaknesses (which we aim to address in the future) includes: - - Doesn't look fancy. + - Doesn't look fancy by default. - Limited layout features, intricate layouts are typically crafted in code. @@ -131,7 +132,7 @@ CODE - MOUSE CONTROLS - Mouse wheel: Scroll vertically. - - SHIFT+Mouse wheel: Scroll horizontally. + - Shift+Mouse wheel: Scroll horizontally. - Click [X]: Close a window, available when 'bool* p_open' is passed to ImGui::Begin(). - Click ^, Double-Click title: Collapse window. - Drag on corner/border: Resize window (double-click to auto fit window to its contents). @@ -139,23 +140,24 @@ CODE - Left-click outside popup: Close popup stack (right-click over underlying popup: Partially close popup stack). - TEXT EDITOR - - Hold SHIFT or Drag Mouse: Select text. - - CTRL+Left/Right: Word jump. - - CTRL+Shift+Left/Right: Select words. - - CTRL+A or Double-Click: Select All. - - CTRL+X, CTRL+C, CTRL+V: Use OS clipboard. - - CTRL+Z Undo. - - CTRL+Y or CTRL+Shift+Z: Redo. + - Hold Shift or Drag Mouse: Select text. + - Ctrl+Left/Right: Word jump. + - Ctrl+Shift+Left/Right: Select words. + - Ctrl+A or Double-Click: Select All. + - Ctrl+X, Ctrl+C, Ctrl+V: Use OS clipboard. + - Ctrl+Z Undo. + - Ctrl+Y or Ctrl+Shift+Z: Redo. - ESCAPE: Revert text to its original value. - - On OSX, controls are automatically adjusted to match standard OSX text editing 2ts and behaviors. + - On macOS, controls are automatically adjusted to match standard macOS text editing and behaviors. + (for 99% of shortcuts, Ctrl is replaced by Cmd on macOS). - KEYBOARD CONTROLS - Basic: - - Tab, SHIFT+Tab Cycle through text editable fields. - - CTRL+Tab, CTRL+Shift+Tab Cycle through windows. - - CTRL+Click Input text into a Slider or Drag widget. + - Tab, Shift+Tab Cycle through text editable fields. + - Ctrl+Tab, Ctrl+Shift+Tab Cycle through windows. + - Ctrl+Click Input text into a Slider or Drag widget. - Extended features with `io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard`: - - Tab, SHIFT+Tab: Cycle through every items. + - Tab, Shift+Tab: Cycle through every items. - Arrow keys Move through items using directional navigation. Tweak value. - Arrow keys + Alt, Shift Tweak slower, tweak faster (when using arrow keys). - Enter Activate item (prefer text input when possible). @@ -164,7 +166,7 @@ CODE - Page Up, Page Down Previous page, next page. - Home, End Scroll to top, scroll to bottom. - Alt Toggle between scrolling layer and menu layer. - - CTRL+Tab then Ctrl+Arrows Move window. Hold SHIFT to resize instead of moving. + - Ctrl+Tab then Ctrl+Arrows Move window. Hold Shift to resize instead of moving. - Output when ImGuiConfigFlags_NavEnableKeyboard set, - io.WantCaptureKeyboard flag is set when keyboard is claimed. - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. @@ -200,7 +202,7 @@ CODE READ FIRST ---------- - - Remember to check the wonderful Wiki (https://github.com/ocornut/imgui/wiki) + - Remember to check the wonderful Wiki: https://github.com/ocornut/imgui/wiki - Your code creates the UI every frame of your application loop, if your code doesn't run the UI is gone! The UI can be highly dynamic, there are no construction or destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, fewer bugs. @@ -274,7 +276,8 @@ CODE HOW A SIMPLE APPLICATION MAY LOOK LIKE -------------------------------------- - EXHIBIT 1: USING THE EXAMPLE BACKENDS (= imgui_impl_XXX.cpp files from the backends/ folder). + + USING THE EXAMPLE BACKENDS (= imgui_impl_XXX.cpp files from the backends/ folder). The sub-folders in examples/ contain examples applications following this structure. // Application init: create a dear imgui context, setup some options, load fonts @@ -310,7 +313,27 @@ CODE ImGui_ImplWin32_Shutdown(); ImGui::DestroyContext(); - EXHIBIT 2: IMPLEMENTING CUSTOM BACKEND / CUSTOM ENGINE + To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest of your application, + you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags! + Please read the FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" about this. + + +USING CUSTOM BACKEND / CUSTOM ENGINE +------------------------------------ + +IMPLEMENTING YOUR PLATFORM BACKEND: + -> see https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md for basic instructions. + -> the Platform backends in impl_impl_XXX.cpp files contain many implementations. + +IMPLEMENTING YOUR RenderDrawData() function: + -> see https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md + -> the Renderer Backends in impl_impl_XXX.cpp files contain many implementations of a ImGui_ImplXXXX_RenderDrawData() function. + +IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: + -> see https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md + -> the Renderer Backends in impl_impl_XXX.cpp files contain many implementations of a ImGui_ImplXXXX_UpdateTexture() function. + + Basic application/backend skeleton: // Application init: create a Dear ImGui context, setup some options, load fonts ImGui::CreateContext(); @@ -319,7 +342,7 @@ CODE io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable keyboard controls // TODO: Load TTF/OTF fonts if you don't want to use the default font. - io.Fonts->AddFontFromFileTTF("NotoSans.ttf", 18.0f); + io.Fonts->AddFontFromFileTTF("NotoSans.ttf"); // Application main loop while (true) @@ -361,95 +384,6 @@ CODE // Shutdown ImGui::DestroyContext(); - To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest of your application, - you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags! - Please read the FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" about this. - - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE - --------------------------------------------- - The backends in impl_impl_XXX.cpp files contain many working implementations of a rendering function. - - void MyImGuiBackend_UpdateTexture(ImTextureData* tex) - { - if (tex->Status == ImTextureStatus_WantCreate) - { - // Width/Height/Pixels> - tex->SetTexID(xxxx); // specify backend-specific ImTextureID identifier - tex->SetStatus(ImTextureStatus_OK); - tex->BackendUserData = xxxx; // store more backend data - } - if (tex->Status == ImTextureStatus_WantUpdates) - { - // UpdateRect> - tex->SetStatus(ImTextureStatus_OK); - } - if (tex->Status == ImTextureStatus_WantDestroy) - { - // - tex->SetTexID(ImTextureID_Invalid); - tex->SetStatus(ImTextureStatus_Destroyed); - } - } - - void MyImGuiBackend_RenderDrawData(ImDrawData* draw_data) - { - if (draw_data->Textures != nullptr) - for (ImTextureData* tex : *draw_data->Textures) - if (tex->Status != ImTextureStatus_OK) - MyImGuiBackend_UpdateTexture(tex); - - - // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled - // TODO: Setup texture sampling state: sample with bilinear filtering (NOT point/nearest filtering). Use 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines;' to allow point/nearest filtering. - // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize - // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize - // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color. - ImVec2 clip_off = draw_data->DisplayPos; - for (int n = 0; n < draw_data->CmdListsCount; n++) - { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by Dear ImGui - const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by Dear ImGui - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) - { - const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; - if (pcmd->UserCallback) - { - if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) - MyEngineResetRenderState(); - else - pcmd->UserCallback(cmd_list, pcmd); - } - else - { - // Project scissor/clipping rectangles into framebuffer space - ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); - ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); - if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) - continue; - - // We are using scissoring to clip some objects. All low-level graphics API should support it. - // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches - // (some elements visible outside their bounds) but you can fix that once everything else works! - // - Clipping coordinates are provided in imgui coordinates space: - // - For a given viewport, draw_data->DisplayPos == viewport->Pos and draw_data->DisplaySize == viewport->Size - // - In a single viewport application, draw_data->DisplayPos == (0,0) and draw_data->DisplaySize == io.DisplaySize, but always use GetMainViewport()->Pos/Size instead of hardcoding those values. - // - In the interest of supporting multi-viewport applications (see 'docking' branch on github), - // always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space. - // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min) - MyEngineSetScissor(clip_min.x, clip_min.y, clip_max.x, clip_max.y); - - // The texture for the draw call is specified by pcmd->GetTexID(). - // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization. - MyEngineBindTexture((MyTexture*)pcmd->GetTexID()); - - // Render 'pcmd->ElemCount/3' indexed triangles. - // By default the indices ImDrawIdx are 16-bit, you can change them to 32-bit in imconfig.h if your engine doesn't support 16-bit indices. - MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer + pcmd->IdxOffset, vtx_buffer, pcmd->VtxOffset); - } - } - } - } API BREAKING CHANGES @@ -460,7 +394,39 @@ CODE 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. - - 2025/06/25 (1.92.0) - layout: commented out legacy ErrorCheckUsingSetCursorPosToExtendParentBoundaries() fallback obsoleted in 1.89 (August 2022) which allowed a SetCursorPos()/SetCursorScreenPos() call WITHOUT AN ITEM + - 2025/12/23 (1.92.6) - Fonts:AddFontDefault() now automatically selects an embedded font between the new scalable AddFontDefaultVector() and the classic pixel-clean AddFontDefaultBitmap(). + The default selection is based on (style.FontSizeBase * FontScaleMain * FontScaleDpi) reaching a small threshold. Prefer calling either based on your own logic. You can call AddFontDefaultBitmap() to ensure legacy behavior. + - 2025/12/23 (1.92.6) - Fonts: removed ImFontConfig::PixelSnapV added in 1.92 which turns out is unnecessary (and misdocumented). Post-rescale GlyphOffset is always rounded. + - 2025/12/17 (1.92.6) - Renamed helper macro IM_ARRAYSIZE() -> IM_COUNTOF(). Kept redirection/legacy name for now. + - 2025/12/11 (1.92.6) - Hashing: handling of "###" operator to reset to seed within a string identifier doesn't include the "###" characters in the output hash anymore. + - Before: GetID("Hello###World") == GetID("###World") != GetID("World"); + - Now: GetID("Hello###World") == GetID("###World") == GetID("World"); + - This has the property of facilitating concatenating and manipulating identifiers using "###", and will allow fixing other dangling issues. + - This will invalidate hashes (stored in .ini data) for Tables and Windows that are using the "###" operators. (#713, #1698) + - 2025/11/24 (1.92.6) - Fonts: Fixed handling of `ImFontConfig::FontDataOwnedByAtlas = false` which did erroneously make a copy of the font data, essentially defeating the purpose of this flag and wasting memory. + (trivia: undetected since July 2015, this is perhaps the oldest bug in Dear ImGui history, albeit for a rarely used feature, see #9086) + HOWEVER, fixing this bug is likely to surface bugs in user code using `FontDataOwnedByAtlas = false`. + - Prior to 1.92, font data only needed to be available during the atlas->AddFontXXX() call. + - Since 1.92, font data needs to available until atlas->RemoveFont(), or more typically until a shutdown of the owning context or font atlas. + - The fact that handling of `FontDataOwnedByAtlas = false` was broken bypassed the issue altogether. + - 2025/11/06 (1.92.5) - BeginChild: commented out some legacy names which were obsoleted in 1.90.0 (Nov 2023), 1.90.9 (July 2024), 1.91.1 (August 2024): + - ImGuiChildFlags_Border --> ImGuiChildFlags_Borders + - ImGuiWindowFlags_NavFlattened --> ImGuiChildFlags_NavFlattened (moved to ImGuiChildFlags). BeginChild(name, size, 0, ImGuiWindowFlags_NavFlattened) --> BeginChild(name, size, ImGuiChildFlags_NavFlattened, 0) + - ImGuiWindowFlags_AlwaysUseWindowPadding --> ImGuiChildFlags_AlwaysUseWindowPadding (moved to ImGuiChildFlags). BeginChild(name, size, 0, ImGuiWindowFlags_AlwaysUseWindowPadding) --> BeginChild(name, size, ImGuiChildFlags_AlwaysUseWindowPadding, 0) + - 2025/11/06 (1.92.5) - Keys: commented out legacy names which were obsoleted in 1.89.0 (August 2022): + - ImGuiKey_ModCtrl --> ImGuiMod_Ctrl + - ImGuiKey_ModShift --> ImGuiMod_Shift + - ImGuiKey_ModAlt --> ImGuiMod_Alt + - ImGuiKey_ModSuper --> ImGuiMod_Super + - 2025/11/06 (1.92.5) - IO: commented out legacy io.ClearInputCharacters() obsoleted in 1.89.8 (Aug 2023). Calling io.ClearInputKeys() is enough. + - 2025/11/06 (1.92.5) - Commented out legacy SetItemAllowOverlap() obsoleted in 1.89.7: this never worked right. Use SetNextItemAllowOverlap() _before_ item instead. + - 2025/10/14 (1.92.4) - TreeNode, Selectable, Clipper: commented out legacy names which were obsoleted in 1.89.7 (July 2023) and 1.89.9 (Sept 2023); + - ImGuiTreeNodeFlags_AllowItemOverlap --> ImGuiTreeNodeFlags_AllowOverlap + - ImGuiSelectableFlags_AllowItemOverlap --> ImGuiSelectableFlags_AllowOverlap + - ImGuiListClipper::IncludeRangeByIndices() --> ImGuiListClipper::IncludeItemsByIndex() + - 2025/08/08 (1.92.2) - Backends: SDL_GPU3: Changed ImTextureID type from SDL_GPUTextureSamplerBinding* to SDL_GPUTexture*, which is more natural and easier for user to manage. If you need to change the current sampler, you can access the ImGui_ImplSDLGPU3_RenderState struct. (#8866, #8163, #7998, #7988) + - 2025/07/31 (1.92.2) - Tabs: Renamed ImGuiTabBarFlags_FittingPolicyResizeDown to ImGuiTabBarFlags_FittingPolicyShrink. Kept inline redirection enum (will obsolete). + - 2025/06/25 (1.92.0) - Layout: commented out legacy ErrorCheckUsingSetCursorPosToExtendParentBoundaries() fallback obsoleted in 1.89 (August 2022) which allowed a SetCursorPos()/SetCursorScreenPos() call WITHOUT AN ITEM to extend parent window/cell boundaries. Replaced with assert/tooltip that would already happens if previously using IMGUI_DISABLE_OBSOLETE_FUNCTIONS. (#5548, #4510, #3355, #1760, #1490, #4152, #150) - Incorrect way to make a window content size 200x200: Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End(); @@ -484,9 +450,12 @@ CODE - To use old behavior: use 'ImGui::PushFont(font, font->LegacySize)' at call site. - Kept inline single parameter function. Will obsolete. - Fonts: **IMPORTANT** on Font Merging: - - When searching for a glyph in multiple merged fonts: font inputs are now scanned in orderfor the first font input which the desired glyph. This is technically a different behavior than before! - - e.g. If you are merging fonts you may have glyphs that you expected to load from Font Source 2 which exists in Font Source 1. After the update and when using a new backend, those glyphs may now loaded from Font Source 1! - - You can use `ImFontConfig::GlyphExcludeRanges[]` to specify ranges to ignore in given Input: + - When searching for a glyph in multiple merged fonts: we search for the FIRST font source which contains the desired glyph. + Because the user doesn't need to provide glyph ranges any more, it is possible that a glyph that you expected to fetch from a secondary/merged icon font may be erroneously fetched from the primary font. + - When searching for a glyph in multiple merged fonts: we now search for the FIRST font source which contains the desired glyph. This is technically a different behavior than before! + - e.g. If you are merging fonts you may have glyphs that you expected to load from Font Source 2 which exists in Font Source 1. + After the update and when using a new backend, those glyphs may now loaded from Font Source 1! + - We added `ImFontConfig::GlyphExcludeRanges[]` to specify ranges to exclude from a given font source: // Add Font Source 1 but ignore ICON_MIN_FA..ICON_MAX_FA range static ImWchar exclude_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; ImFontConfig cfg1; @@ -496,7 +465,9 @@ CODE ImFontConfig cfg2; cfg2.MergeMode = true; io.Fonts->AddFontFromFileTTF("FontAwesome4.ttf", 0.0f, &cfg2); - - You can use `Metrics/Debugger->Fonts->Font->Input Glyphs Overlap Detection Tool` to see list of glyphs available in multiple font sources. This can facilitate unde + - You can use `Metrics/Debugger->Fonts->Font->Input Glyphs Overlap Detection Tool` to see list of glyphs available in multiple font sources. This can facilitate understanding which font input is providing which glyph. + - Fonts: **IMPORTANT** on Thread Safety: + - A few functions such as font->CalcTextSizeA() were, by sheer luck (== accidentally) thread-safe even thou we had never provided that guarantee. They are definitively not thread-safe anymore as new glyphs may be loaded. - Fonts: ImFont::FontSize was removed and does not make sense anymore. ImFont::LegacySize is the size passed to AddFont(). - Fonts: Removed support for PushFont(NULL) which was a shortcut for "default font". - Fonts: Renamed/moved 'io.FontGlobalScale' to 'style.FontScaleMain'. @@ -505,20 +476,23 @@ CODE - Fonts: ImFontConfig::OversampleH/OversampleV default to automatic (== 0) since v1.91.8. It is quite important you keep it automatic until we decide if we want to provide a way to express finer policy, otherwise you will likely waste texture space when using large glyphs. Note that the imgui_freetype backend doesn't use and does not need oversampling. - Fonts: specifying glyph ranges is now unnecessary. The value of ImFontConfig::GlyphRanges[] is only useful for legacy backends. All GetGlyphRangesXXXX() functions are now marked obsolete: GetGlyphRangesDefault(), GetGlyphRangesGreek(), GetGlyphRangesKorean(), GetGlyphRangesJapanese(), GetGlyphRangesChineseSimplifiedCommon(), GetGlyphRangesChineseFull(), GetGlyphRangesCyrillic(), GetGlyphRangesThai(), GetGlyphRangesVietnamese(). - Fonts: removed ImFontAtlas::TexDesiredWidth to enforce a texture width. (#327) - - Fonts: if you create and manage ImFontAtlas instances yourself (instead of relying on ImGuiContext to create one, you'll need to call ImFontAtlasUpdateNewFrame() yourself. An assert will trigger if you don't. + - Fonts: if you create and manage ImFontAtlas instances yourself (instead of relying on ImGuiContext to create one), you'll need to call ImFontAtlasUpdateNewFrame() yourself. An assert will trigger if you don't. - Fonts: obsolete ImGui::SetWindowFontScale() which is not useful anymore. Prefer using 'PushFont(NULL, style.FontSizeBase * factor)' or to manipulate other scaling factors. - Fonts: obsoleted ImFont::Scale which is not useful anymore. - Fonts: generally reworked Internals of ImFontAtlas and ImFont. While in theory a vast majority of users shouldn't be affected, some use cases or extensions might be. Among other things: - ImDrawCmd::TextureId has been changed to ImDrawCmd::TexRef. + - ImFontAtlas::TexID has been changed to ImFontAtlas::TexRef. - ImFontAtlas::ConfigData[] has been renamed to ImFontAtlas::Sources[] - ImFont::ConfigData[], ConfigDataCount has been renamed to Sources[], SourceCount. - Each ImFont has a number of ImFontBaked instances corresponding to actively used sizes. ImFont::GetFontBaked(size) retrieves the one for a given size. - Fields moved from ImFont to ImFontBaked: IndexAdvanceX[], Glyphs[], Ascent, Descent, FindGlyph(), FindGlyphNoFallback(), GetCharAdvance(). + - Fields moved from ImFontAtlas to ImFontAtlas->Tex: ImFontAtlas::TexWidth => TexData->Width, ImFontAtlas::TexHeight => TexData->Height, ImFontAtlas::TexPixelsAlpha8/TexPixelsRGBA32 => TexData->GetPixels(). - Widget code may use ImGui::GetFontBaked() instead of ImGui::GetFont() to access font data for current font at current font size (and you may use font->GetFontBaked(size) to access it for any other size.) - Fonts: (users of imgui_freetype): renamed ImFontAtlas::FontBuilderFlags to ImFontAtlas::FontLoaderFlags. Renamed ImFontConfig::FontBuilderFlags to ImFontConfig::FontLoaderFlags. Renamed ImGuiFreeTypeBuilderFlags to ImGuiFreeTypeLoaderFlags. If you used runtime imgui_freetype selection rather than the default IMGUI_ENABLE_FREETYPE compile-time option: Renamed/reworked ImFontBuilderIO into ImFontLoader. Renamed ImGuiFreeType::GetBuilderForFreeType() to ImGuiFreeType::GetFontLoader(). - old: io.Fonts->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType() - - new: io.Fonts.FontLoader = ImGuiFreeType::GetFontLoader() + - new: io.Fonts->FontLoader = ImGuiFreeType::GetFontLoader() + - new: io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader()) to change dynamically at runtime [from 1.92.1] - Fonts: (users of custom rectangles, see #8466): Renamed AddCustomRectRegular() to AddCustomRect(). Added GetCustomRect() as a replacement for GetCustomRectByIndex() + CalcCustomRectUV(). - The output type of GetCustomRect() is now ImFontAtlasRect, which include UV coordinates. X->x, Y->y, Width->w, Height->h. - old: @@ -565,6 +539,7 @@ CODE - 2024/11/06 (1.91.5) - commented/obsoleted out pre-1.87 IO system (equivalent to using IMGUI_DISABLE_OBSOLETE_KEYIO or IMGUI_DISABLE_OBSOLETE_FUNCTIONS before) - io.KeyMap[] and io.KeysDown[] are removed (obsoleted February 2022). - io.NavInputs[] and ImGuiNavInput are removed (obsoleted July 2022). + - GetKeyIndex() is removed (obsoleted March 2022). The indirection is now unnecessary. - pre-1.87 backends are not supported: - backends need to call io.AddKeyEvent(), io.AddMouseEvent() instead of writing to io.KeysDown[], io.MouseDown[] fields. - backends need to call io.AddKeyAnalogEvent() for gamepad values instead of writing to io.NavInputs[] fields. @@ -584,7 +559,7 @@ CODE in doubt it is almost always better to do an intermediate intptr_t cast, since it allows casting any pointer/integer type without warning: - May warn: ImGui::Image((void*)MyTextureData, ...); - May warn: ImGui::Image((void*)(intptr_t)MyTextureData, ...); - - Won't warn: ImGui::Image((ImTextureID)(intptr_t)MyTextureData), ...); + - Won't warn: ImGui::Image((ImTextureID)(intptr_t)MyTextureData, ...); - note that you can always define ImTextureID to be your own high-level structures (with dedicated constructors) if you like. - 2024/10/03 (1.91.3) - drags: treat v_min==v_max as a valid clamping range when != 0.0f. Zero is a still special value due to legacy reasons, unless using ImGuiSliderFlags_ClampZeroRange. (#7968, #3361, #76) - drags: extended behavior of ImGuiSliderFlags_AlwaysClamp to include _ClampZeroRange. It considers v_min==v_max==0.0f as a valid clamping range (aka edits not allowed). @@ -871,7 +846,7 @@ CODE - ShowTestWindow() -> use ShowDemoWindow() - IsRootWindowFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow) - IsRootWindowOrAnyChildFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) - - SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f) + - SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f)) - GetItemsLineHeightWithSpacing() -> use GetFrameHeightWithSpacing() - ImGuiCol_ChildWindowBg -> use ImGuiCol_ChildBg - ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding @@ -1113,6 +1088,7 @@ CODE associated with it. Q: What is this library called? + Q: What is the difference between Dear ImGui and traditional UI toolkits? Q: Which version should I get? >> This library is called "Dear ImGui", please don't call it "ImGui" :) >> See https://www.dearimgui.com/faq for details. @@ -1287,13 +1263,13 @@ CODE #endif // Debug options -#define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Hold CTRL to display for all candidates. CTRL+Arrow to change last direction. +#define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Hold Ctrl to display for all candidates. Ctrl+Arrow to change last direction. #define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window // Default font size if unspecified in both style.FontSizeBase and AddFontXXX() calls. -static const float FONT_DEFAULT_SIZE = 20.0f; +static const float FONT_DEFAULT_SIZE_BASE = 20.0f; -// When using CTRL+TAB (or Gamepad Square+L/R) we delay the visual a little in order to reduce visual noise doing a fast switch. +// When using Ctrl+Tab (or Gamepad Square+L/R) we delay the visual a little in order to reduce visual noise doing a fast switch. static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear static const float NAV_ACTIVATE_HIGHLIGHT_TIMER = 0.10f; // Time to highlight an item activated by a shortcut. @@ -1349,7 +1325,7 @@ static float NavUpdatePageUpPageDown(); static inline void NavUpdateAnyRequestFlag(); static void NavUpdateCreateWrappingRequest(); static void NavEndFrame(); -static bool NavScoreItem(ImGuiNavItemData* result); +static bool NavScoreItem(ImGuiNavItemData* result, const ImRect& nav_bb); static void NavApplyItemToResult(ImGuiNavItemData* result); static void NavProcessItem(); static void NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags); @@ -1364,7 +1340,7 @@ static void ErrorCheckNewFrameSanityChecks(); static void ErrorCheckEndFrameSanityChecks(); #ifndef IMGUI_DISABLE_DEBUG_TOOLS static void UpdateDebugToolItemPicker(); -static void UpdateDebugToolStackQueries(); +static void UpdateDebugToolItemPathQuery(); static void UpdateDebugToolFlashStyleColor(); #endif @@ -1380,7 +1356,7 @@ static void UpdateFontsEndFrame(); static void UpdateTexturesNewFrame(); static void UpdateTexturesEndFrame(); static void UpdateSettings(); -static int UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect); +static int UpdateWindowManualResize(ImGuiWindow* window, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect); static void RenderWindowOuterBorders(ImGuiWindow* window); static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size); static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open); @@ -1471,12 +1447,15 @@ ImGuiStyle::ImGuiStyle() ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1). ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar + ScrollbarPadding = 2.0f; // Padding of scrollbar grab within its frame (same for both axises) GrabMinSize = 12.0f; // Minimum width/height of a grab box for slider/scrollbar 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. ImageBorderSize = 0.0f; // Thickness of border around tabs. 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. + TabMinWidthShrink = 80.0f; // Minimum tab width after shrinking, when using ImGuiTabBarFlags_FittingPolicyMixed policy. TabCloseButtonMinWidthSelected = -1.0f; // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width. TabCloseButtonMinWidthUnselected = 0.0f; // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width. FLT_MAX: never show close button when unselected. TabBarBorderSize = 1.0f; // Thickness of tab-bar separator, which takes on the tab active color to denote focus. @@ -1486,6 +1465,10 @@ 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. + 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. + ColorMarkerSize = 3.0f; // Size of R/G/B/A color markers for ColorEdit4() and for Drags/Sliders when using ImGuiSliderFlags_ColorMarkers. 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. @@ -1540,15 +1523,22 @@ void ImGuiStyle::ScaleAllSizes(float scale_factor) ColumnsMinSpacing = ImCeil(ColumnsMinSpacing * scale_factor); ScrollbarSize = ImCeil(ScrollbarSize * scale_factor); ScrollbarRounding = ImCeil(ScrollbarRounding * scale_factor); + ScrollbarPadding = ImCeil(ScrollbarPadding * scale_factor); GrabMinSize = ImCeil(GrabMinSize * scale_factor); GrabRounding = ImCeil(GrabRounding * scale_factor); LogSliderDeadzone = ImCeil(LogSliderDeadzone * scale_factor); ImageBorderSize = ImCeil(ImageBorderSize * scale_factor); TabRounding = ImCeil(TabRounding * scale_factor); + TabMinWidthBase = ImCeil(TabMinWidthBase * scale_factor); + TabMinWidthShrink = ImCeil(TabMinWidthShrink * scale_factor); TabCloseButtonMinWidthSelected = (TabCloseButtonMinWidthSelected > 0.0f && TabCloseButtonMinWidthSelected != FLT_MAX) ? ImCeil(TabCloseButtonMinWidthSelected * scale_factor) : TabCloseButtonMinWidthSelected; TabCloseButtonMinWidthUnselected = (TabCloseButtonMinWidthUnselected > 0.0f && TabCloseButtonMinWidthUnselected != FLT_MAX) ? ImCeil(TabCloseButtonMinWidthUnselected * scale_factor) : TabCloseButtonMinWidthUnselected; TabBarOverlineSize = ImCeil(TabBarOverlineSize * scale_factor); TreeLinesRounding = ImCeil(TreeLinesRounding * scale_factor); + DragDropTargetRounding = ImCeil(DragDropTargetRounding * scale_factor); + DragDropTargetBorderSize = ImCeil(DragDropTargetBorderSize * scale_factor); + DragDropTargetPadding = ImCeil(DragDropTargetPadding * scale_factor); + ColorMarkerSize = ImCeil(ColorMarkerSize * scale_factor); SeparatorTextPadding = ImVec2(ImCeil(SeparatorTextPadding.x * scale_factor), ImCeil(SeparatorTextPadding.y * scale_factor)); DisplayWindowPadding = ImVec2(ImCeil(DisplayWindowPadding.x * scale_factor), ImCeil(DisplayWindowPadding.y * scale_factor)); DisplaySafeAreaPadding = ImVec2(ImCeil(DisplaySafeAreaPadding.x * scale_factor), ImCeil(DisplaySafeAreaPadding.y * scale_factor)); @@ -1559,7 +1549,7 @@ ImGuiIO::ImGuiIO() { // Most fields are initialized with zero memset(this, 0, sizeof(*this)); - IM_STATIC_ASSERT(IM_ARRAYSIZE(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_ARRAYSIZE(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT); + IM_STATIC_ASSERT(IM_COUNTOF(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_COUNTOF(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT); // Settings ConfigFlags = ImGuiConfigFlags_None; @@ -1631,8 +1621,8 @@ ImGuiIO::ImGuiIO() MousePos = ImVec2(-FLT_MAX, -FLT_MAX); MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX); MouseSource = ImGuiMouseSource_Mouse; - for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f; - for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; } + for (int i = 0; i < IM_COUNTOF(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f; + for (int i = 0; i < IM_COUNTOF(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; } AppAcceptingEvents = true; } @@ -1691,14 +1681,15 @@ void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c) AddInputCharacter((unsigned)cp); } -void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) +void ImGuiIO::AddInputCharactersUTF8(const char* str) { if (!AppAcceptingEvents) return; - while (*utf8_chars != 0) + const char* str_end = str + strlen(str); + while (*str != 0) { unsigned int c = 0; - utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL); + str += ImTextCharFromUtf8(&c, str, str_end); AddInputCharacter(c); } } @@ -1739,7 +1730,7 @@ void ImGuiIO::ClearInputMouse() key_data->DownDurationPrev = -1.0f; } MousePos = ImVec2(-FLT_MAX, -FLT_MAX); - for (int n = 0; n < IM_ARRAYSIZE(MouseDown); n++) + for (int n = 0; n < IM_COUNTOF(MouseDown); n++) { MouseDown[n] = false; MouseDownDuration[n] = MouseDownDurationPrev[n] = -1.0f; @@ -1747,15 +1738,6 @@ void ImGuiIO::ClearInputMouse() MouseWheel = MouseWheelH = 0.0f; } -// Removed this as it is ambiguous/misleading and generally incorrect to use with the existence of a higher-level input queue. -// Current frame character buffer is now also cleared by ClearInputKeys(). -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS -void ImGuiIO::ClearInputCharacters() -{ - InputQueueCharacters.resize(0); -} -#endif - static ImGuiInputEvent* FindLatestInputEvent(ImGuiContext* ctx, ImGuiInputEventType type, int arg = -1) { ImGuiContext& g = *ctx; @@ -2066,7 +2048,7 @@ bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f; bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f; bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f; - return ((b1 == b2) && (b2 == b3)); + return (b1 == b2) && (b2 == b3); } void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w) @@ -2120,7 +2102,7 @@ void ImStrncpy(char* dst, const char* src, size_t count) if (count < 1) return; if (count > 1) - strncpy(dst, src, count - 1); + strncpy(dst, src, count - 1); // FIXME-OPT: strncpy not only doesn't guarantee 0-termination, it also always writes the whole array dst[count - 1] = 0; } @@ -2398,11 +2380,8 @@ ImGuiID ImHashData(const void* data_p, size_t data_size, ImGuiID seed) #endif } -// Zero-terminated string hash, with support for ### to reset back to seed value -// We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed. -// Because this syntax is rarely used we are optimizing for the common case. -// - If we reach ### in the string we discard the hash so far and reset to the seed. -// - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build) +// Zero-terminated string hash, with support for ### to reset back to seed value. +// e.g. "label###id" outputs the same hash as "id" (and "label" is generally displayed by the UI functions) // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements. ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed) { @@ -2414,11 +2393,16 @@ ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed) #endif if (data_size != 0) { - while (data_size-- != 0) + while (data_size-- > 0) { unsigned char c = *data++; if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#') + { crc = seed; + data += 2; + data_size -= 2; + continue; + } #ifndef IMGUI_ENABLE_SSE4_2_CRC crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; #else @@ -2431,7 +2415,11 @@ ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed) while (unsigned char c = *data++) { if (c == '#' && data[0] == '#' && data[1] == '#') + { crc = seed; + data += 2; + continue; + } #ifndef IMGUI_ENABLE_SSE4_2_CRC crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; #else @@ -2442,6 +2430,17 @@ ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed) return ~crc; } +// Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() +// FIXME-OPT: This is not designed to be optimal. Use with care. +const char* ImHashSkipUncontributingPrefix(const char* label) +{ + const char* result = label; + while (unsigned char c = *label++) + if (c == '#' && label[0] == '#' && label[1] == '#') + result = label + 2; + return result; +} + //----------------------------------------------------------------------------- // [SECTION] MISC HELPERS/UTILITIES (File functions) //----------------------------------------------------------------------------- @@ -2461,7 +2460,7 @@ ImFileHandle ImFileOpen(const char* filename, const char* mode) // We don't rely on current ImGuiContext as this is implied to be a helper function which doesn't depend on it (see #7314). wchar_t local_temp_stack[FILENAME_MAX]; ImVector local_temp_heap; - if (filename_wsize + mode_wsize > IM_ARRAYSIZE(local_temp_stack)) + if (filename_wsize + mode_wsize > IM_COUNTOF(local_temp_stack)) local_temp_heap.resize(filename_wsize + mode_wsize); wchar_t* filename_wbuf = local_temp_heap.Data ? local_temp_heap.Data : local_temp_stack; wchar_t* mode_wbuf = filename_wbuf + filename_wsize; @@ -2541,6 +2540,7 @@ int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* int len = lengths[*(const unsigned char*)in_text >> 3]; int wanted = len + (len ? 0 : 1); + // IMPORTANT: if in_text_end == NULL it assume we have enough space! if (in_text_end == NULL) in_text_end = in_text + wanted; // Max length, nulls will be taken into account. @@ -2647,11 +2647,11 @@ static inline int ImTextCharToUtf8_inline(char* buf, int buf_size, unsigned int return 0; } -const char* ImTextCharToUtf8(char out_buf[5], unsigned int c) +int ImTextCharToUtf8(char out_buf[5], unsigned int c) { int count = ImTextCharToUtf8_inline(out_buf, 5, c); out_buf[count] = 0; - return out_buf; + return count; } // Not optimal but we very rarely use this function. @@ -2700,17 +2700,29 @@ int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_e return bytes_count; } -const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_text_curr) +const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_p) { - while (in_text_curr > in_text_start) + while (in_p > in_text_start) { - in_text_curr--; - if ((*in_text_curr & 0xC0) != 0x80) - return in_text_curr; + in_p--; + if ((*in_p & 0xC0) != 0x80) + return in_p; } return in_text_start; } +const char* ImTextFindValidUtf8CodepointEnd(const char* in_text_start, const char* in_text_end, const char* in_p) +{ + if (in_text_start == in_p) + return in_text_start; + const char* prev = ImTextFindPreviousUtf8Codepoint(in_text_start, in_p); + unsigned int prev_c; + int prev_c_len = ImTextCharFromUtf8(&prev_c, prev, in_text_end); + if (prev_c != IM_UNICODE_CODEPOINT_INVALID && prev_c_len <= (int)(in_p - prev)) + return in_p; + return prev; +} + int ImTextCountLines(const char* in_text, const char* in_text_end) { if (in_text_end == NULL) @@ -2963,7 +2975,7 @@ ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) //-V1077 CountGrep = 0; if (default_filter) { - ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf)); + ImStrncpy(InputBuf, default_filter, IM_COUNTOF(InputBuf)); Build(); } } @@ -2972,7 +2984,7 @@ bool ImGuiTextFilter::Draw(const char* label, float width) { if (width != 0.0f) ImGui::SetNextItemWidth(width); - bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf)); + bool value_changed = ImGui::InputText(label, InputBuf, IM_COUNTOF(InputBuf)); if (value_changed) Build(); return value_changed; @@ -3118,19 +3130,21 @@ void ImGuiTextBuffer::appendfv(const char* fmt, va_list args) va_end(args_copy); } +IM_MSVC_RUNTIME_CHECKS_OFF void ImGuiTextIndex::append(const char* base, int old_size, int new_size) { IM_ASSERT(old_size >= 0 && new_size >= old_size && new_size >= EndOffset); if (old_size == new_size) return; if (EndOffset == 0 || base[EndOffset - 1] == '\n') - LineOffsets.push_back(EndOffset); + Offsets.push_back(EndOffset); const char* base_end = base + new_size; for (const char* p = base + old_size; (p = (const char*)ImMemchr(p, '\n', base_end - p)) != 0; ) if (++p < base_end) // Don't push a trailing offset on last \n - LineOffsets.push_back((int)(intptr_t)(p - base)); + Offsets.push_back((int)(intptr_t)(p - base)); EndOffset = ImMax(EndOffset, new_size); } +IM_MSVC_RUNTIME_CHECKS_RESTORE //----------------------------------------------------------------------------- // [SECTION] ImGuiListClipper @@ -3141,7 +3155,7 @@ void ImGuiTextIndex::append(const char* base, int old_size, int new_size) static bool GetSkipItemForListClipping() { ImGuiContext& g = *GImGui; - return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems); + return g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems; } static void ImGuiListClipper_SortAndFuseRanges(ImVector& ranges, int offset = 0) @@ -3168,7 +3182,7 @@ static void ImGuiListClipper_SortAndFuseRanges(ImVector& } } -static void ImGuiListClipper_SeekCursorAndSetupPrevLine(float pos_y, float line_height) +static void ImGuiListClipper_SeekCursorAndSetupPrevLine(ImGuiListClipper* clipper, float pos_y, float line_height) { // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor. // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue. @@ -3186,10 +3200,13 @@ static void ImGuiListClipper_SeekCursorAndSetupPrevLine(float pos_y, float line_ { if (table->IsInsideRow) ImGui::TableEndRow(table); - table->RowPosY2 = window->DC.CursorPos.y; const int row_increase = (int)((off_y / line_height) + 0.5f); - //table->CurrentRow += row_increase; // Can't do without fixing TableEndRow() - table->RowBgColorCounter += row_increase; + if (row_increase > 0 && (clipper->Flags & ImGuiListClipperFlags_NoSetTableRowCounters) == 0) // If your clipper item height is != from actual table row height, consider using ImGuiListClipperFlags_NoSetTableRowCounters. See #8886. + { + table->CurrentRow += row_increase; + table->RowBgColorCounter += row_increase; + } + table->RowPosY2 = window->DC.CursorPos.y; } } @@ -3272,7 +3289,7 @@ void ImGuiListClipper::SeekCursorForItem(int item_n) // - StartPosY starts from ItemsFrozen, by adding SeekOffsetY we generally cancel that out (SeekOffsetY == LossynessOffset - ItemsFrozen * ItemsHeight). // - The reason we store SeekOffsetY instead of inferring it, is because we want to allow user to perform Seek after the last step, where ImGuiListClipperData is already done. float pos_y = (float)((double)StartPosY + StartSeekOffsetY + (double)item_n * ItemsHeight); - ImGuiListClipper_SeekCursorAndSetupPrevLine(pos_y, ItemsHeight); + ImGuiListClipper_SeekCursorAndSetupPrevLine(this, pos_y, ItemsHeight); } static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) @@ -3358,10 +3375,12 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) { // Add range selected to be included for navigation const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav); + const int nav_off_min = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0; + const int nav_off_max = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) ? 1 : 0; if (is_nav_request) { - data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringRect.Min.y, g.NavScoringRect.Max.y, 0, 0)); - data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, 0, 0)); + data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringRect.Min.y, g.NavScoringRect.Max.y, nav_off_min, nav_off_max)); + data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, nav_off_min, nav_off_max)); } if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && g.NavTabbingDir == -1) data->Ranges.push_back(ImGuiListClipperRange::FromIndices(clipper->ItemsCount - 1, clipper->ItemsCount)); @@ -3371,7 +3390,6 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) if (g.NavId != 0 && window->NavLastIds[0] == g.NavId) data->Ranges.push_back(ImGuiListClipperRange::FromPositions(nav_rect_abs.Min.y, nav_rect_abs.Max.y, 0, 0)); - // Add visible range float min_y = window->ClipRect.Min.y; float max_y = window->ClipRect.Max.y; @@ -3390,9 +3408,8 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) data->Ranges.push_back(ImGuiListClipperRange::FromPositions(bs->UnclipRect.Min.y, bs->UnclipRect.Max.y, 0, 0)); } - const int off_min = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0; - const int off_max = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) ? 1 : 0; - data->Ranges.push_back(ImGuiListClipperRange::FromPositions(min_y, max_y, off_min, off_max)); + // Add main visible range + data->Ranges.push_back(ImGuiListClipperRange::FromPositions(min_y, max_y, nav_off_min, nav_off_max)); } // Convert position ranges to item index ranges @@ -3455,6 +3472,13 @@ bool ImGuiListClipper::Step() return ret; } +// Generic helper, equivalent to old ImGui::CalcListClipping() but statelesss +void ImGui::CalcClipRectVisibleItemsY(const ImRect& clip_rect, const ImVec2& pos, float items_height, int* out_visible_start, int* out_visible_end) +{ + *out_visible_start = ImMax((int)((clip_rect.Min.y - pos.y) / items_height), 0); + *out_visible_end = ImMax((int)ImCeil((clip_rect.Max.y - pos.y) / items_height), *out_visible_start); +} + //----------------------------------------------------------------------------- // [SECTION] STYLING //----------------------------------------------------------------------------- @@ -3560,11 +3584,14 @@ static const ImGuiStyleVarInfo GStyleVarsInfo[] = { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, CellPadding) }, // ImGuiStyleVar_CellPadding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding + { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollbarPadding) }, // ImGuiStyleVar_ScrollbarPadding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ImageBorderSize) }, // ImGuiStyleVar_ImageBorderSize { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabBorderSize) }, // ImGuiStyleVar_TabBorderSize + { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabMinWidthBase) }, // ImGuiStyleVar_TabMinWidthBase + { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabMinWidthShrink) }, // ImGuiStyleVar_TabMinWidthShrink { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabBarBorderSize) }, // ImGuiStyleVar_TabBarBorderSize { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabBarOverlineSize) }, // ImGuiStyleVar_TabBarOverlineSize { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersAngle)}, // ImGuiStyleVar_TableAngledHeadersAngle @@ -3583,7 +3610,7 @@ static const ImGuiStyleVarInfo GStyleVarsInfo[] = const ImGuiStyleVarInfo* ImGui::GetStyleVarInfo(ImGuiStyleVar idx) { IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT); - IM_STATIC_ASSERT(IM_ARRAYSIZE(GStyleVarsInfo) == ImGuiStyleVar_COUNT); + IM_STATIC_ASSERT(IM_COUNTOF(GStyleVarsInfo) == ImGuiStyleVar_COUNT); return &GStyleVarsInfo[idx]; } @@ -3723,6 +3750,8 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx) case ImGuiCol_TextSelectedBg: return "TextSelectedBg"; case ImGuiCol_TreeLines: return "TreeLines"; case ImGuiCol_DragDropTarget: return "DragDropTarget"; + case ImGuiCol_DragDropTargetBg: return "DragDropTargetBg"; + case ImGuiCol_UnsavedMarker: return "UnsavedMarker"; case ImGuiCol_NavCursor: return "NavCursor"; case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight"; case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg"; @@ -3845,7 +3874,7 @@ void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, cons // Another overly complex function until we reorganize everything into a nice all-in-one helper. // This is made more complex because we have dissociated the layout rectangle (pos_min..pos_max) from 'ellipsis_max_x' which may be beyond it. // This is because in the context of tabs we selectively hide part of the text when the Close Button appears, but we don't want the ellipsis to move. -// (BREAKING) On 2025/04/16 we removed the 'float clip_max_x' parameters which was preceeding 'float ellipsis_max' and was the same value for 99% of users. +// (BREAKING) On 2025/04/16 we removed the 'float clip_max_x' parameters which was preceding 'float ellipsis_max' and was the same value for 99% of users. void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float ellipsis_max_x, const char* text, const char* text_end_full, const ImVec2* text_size_if_known) { ImGuiContext& g = *GImGui; @@ -3923,6 +3952,15 @@ void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding) } } +void ImGui::RenderColorComponentMarker(const ImRect& bb, ImU32 col, float rounding) +{ + if (bb.Min.x + 1 >= bb.Max.x) + return; + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + 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) { ImGuiContext& g = *GImGui; @@ -4069,6 +4107,13 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) InputTextState.Ctx = this; Initialized = false; + WithinFrameScope = WithinFrameScopeWithImplicitWindow = false; + TestEngineHookItems = false; + FrameCount = 0; + FrameCountEnded = FrameCountRendered = -1; + Time = 0.0f; + memset(ContextName, 0, sizeof(ContextName)); + Font = NULL; FontBaked = NULL; FontSize = FontSizeBase = FontBakedScale = FontWeight = CurrentDpiScale = 0.0f; @@ -4076,15 +4121,8 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)(); if (shared_font_atlas == NULL) IO.Fonts->OwnerContext = this; - Time = 0.0f; - FrameCount = 0; - FrameCountEnded = FrameCountRendered = -1; WithinEndChildID = 0; - WithinFrameScope = WithinFrameScopeWithImplicitWindow = false; - GcCompactAll = false; - TestEngineHookItems = false; TestEngine = NULL; - memset(ContextName, 0, sizeof(ContextName)); InputEventsNextMouseSource = ImGuiMouseSource_Mouse; InputEventsNextEventId = 1; @@ -4100,8 +4138,8 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) WheelingWindowStartFrame = WheelingWindowScrolledFrame = -1; WheelingWindowReleaseTimer = 0.0f; - DebugDrawIdConflicts = 0; - DebugHookIdInfo = 0; + DebugDrawIdConflictsId = 0; + DebugHookIdInfoId = 0; HoveredId = HoveredIdPreviousFrame = 0; HoveredIdPreviousFrameItemCount = 0; HoveredIdAllowOverlap = false; @@ -4119,9 +4157,10 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) ActiveIdHasBeenEditedThisFrame = false; ActiveIdFromShortcut = false; ActiveIdClickOffset = ImVec2(-1, -1); - ActiveIdWindow = NULL; ActiveIdSource = ImGuiInputSource_None; + ActiveIdWindow = NULL; ActiveIdMouseButton = -1; + ActiveIdDisabledId = 0; ActiveIdPreviousFrame = 0; memset(&DeactivatedItemData, 0, sizeof(DeactivatedItemData)); memset(&ActiveIdValueOnActivation, 0, sizeof(ActiveIdValueOnActivation)); @@ -4136,6 +4175,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) CurrentFocusScopeId = 0; CurrentItemFlags = ImGuiItemFlags_None; DebugShowGroupRects = false; + GcCompactAll = false; NavCursorVisible = false; NavHighlightItemUnderNav = false; @@ -4190,7 +4230,8 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) DragDropSourceFrameCount = -1; DragDropMouseButton = -1; DragDropTargetId = 0; - DragDropAcceptFlags = ImGuiDragDropFlags_None; + DragDropTargetFullViewport = 0; + DragDropAcceptFlagsCurr = DragDropAcceptFlagsPrev = ImGuiDragDropFlags_None; DragDropAcceptIdCurrRectSurface = 0.0f; DragDropAcceptIdPrev = DragDropAcceptIdCurr = 0; DragDropAcceptFrameCount = -1; @@ -4244,12 +4285,12 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) memset(LocalizationTable, 0, sizeof(LocalizationTable)); LogEnabled = false; + LogLineFirstItem = false; LogFlags = ImGuiLogFlags_None; LogWindow = NULL; LogNextPrefix = LogNextSuffix = NULL; LogFile = NULL; LogLinePosY = FLT_MAX; - LogLineFirstItem = false; LogDepthRef = 0; LogDepthToExpand = LogDepthToExpandDefault = 2; @@ -4287,6 +4328,11 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) memset(TempKeychordName, 0, sizeof(TempKeychordName)); } +ImGuiContext::~ImGuiContext() +{ + IM_ASSERT(Initialized == false && "Forgot to call DestroyContext()?"); +} + void ImGui::Initialize() { ImGuiContext& g = *GImGui; @@ -4307,7 +4353,7 @@ void ImGui::Initialize() TableSettingsAddSettingsHandler(); // Setup default localization table - LocalizeRegisterEntries(GLocalizationEntriesEnUS, IM_ARRAYSIZE(GLocalizationEntriesEnUS)); + LocalizeRegisterEntries(GLocalizationEntriesEnUS, IM_COUNTOF(GLocalizationEntriesEnUS)); // Setup default ImGuiPlatformIO clipboard/IME handlers. g.PlatformIO.Platform_GetClipboardTextFn = Platform_GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations @@ -4321,7 +4367,7 @@ void ImGui::Initialize() g.Viewports.push_back(viewport); g.TempBuffer.resize(1024 * 3 + 1, 0); - // Build KeysMayBeCharInput[] lookup table (1 bool per named key) + // Build KeysMayBeCharInput[] lookup table (1 bit per named key) for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) if ((key >= ImGuiKey_0 && key <= ImGuiKey_9) || (key >= ImGuiKey_A && key <= ImGuiKey_Z) || (key >= ImGuiKey_Keypad0 && key <= ImGuiKey_Keypad9) || key == ImGuiKey_Tab || key == ImGuiKey_Space || key == ImGuiKey_Apostrophe || key == ImGuiKey_Comma || key == ImGuiKey_Minus || key == ImGuiKey_Period @@ -4332,6 +4378,12 @@ void ImGui::Initialize() #ifdef IMGUI_HAS_DOCK #endif + // Print a debug message when running with debug feature IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS because it is very slow. + // DO NOT COMMENT OUT THIS MESSAGE. IT IS DESIGNED TO REMIND YOU THAT IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS SHOULD ONLY BE TEMPORARILY ENABLED. +#ifdef IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS + DebugLog("IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS is enabled.\nMust disable after use! Otherwise Dear ImGui will run slower.\n"); +#endif + // ImDrawList/ImFontAtlas are designed to function without ImGui, and 99% of it works without an ImGui context. // But this link allows us to facilitate/handle a few edge cases better. ImFontAtlas* atlas = g.IO.Fonts; @@ -4352,7 +4404,7 @@ void ImGui::Shutdown() for (ImFontAtlas* atlas : g.FontAtlases) { UnregisterFontAtlas(atlas); - if (atlas->OwnerContext == &g) + if (atlas->RefCount == 0) { atlas->Locked = false; IM_DELETE(atlas); @@ -4409,6 +4461,7 @@ void ImGui::Shutdown() g.ClipboardHandlerData.clear(); g.MenusIdSubmittedThisFrame.clear(); g.InputTextState.ClearFreeMemory(); + g.InputTextLineIndex.clear(); g.InputTextDeactivatedState.ClearFreeMemory(); g.SettingsWindows.clear(); @@ -4429,6 +4482,13 @@ void ImGui::Shutdown() g.Initialized = false; } +// When using multiple context it can be helpful to give name a name. +// (A) Will be visible in debugger, (B) Will be included in all IMGUI_DEBUG_LOG() calls, (C) Should be <= 15 characters long. +void ImGui::SetContextName(ImGuiContext* ctx, const char* name) +{ + ImStrncpy(ctx->ContextName, name, IM_COUNTOF(ctx->ContextName)); +} + // No specific ordering/dependency support, will see as needed ImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook) { @@ -4476,8 +4536,8 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* ctx, const char* name) : DrawListInst(NUL ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); ScrollExpected = ImVec2(0, 0); ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f); - AutoFitFramesX = AutoFitFramesY = -1; AutoPosLastDirection = ImGuiDir_None; + AutoFitFramesX = AutoFitFramesY = -1; SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = 0; SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX); LastFrameActive = -1; @@ -4525,6 +4585,7 @@ void ImGui::GcCompactTransientMiscBuffers() ImGuiContext& g = *GImGui; g.ItemFlagsStack.clear(); g.GroupStack.clear(); + g.InputTextLineIndex.clear(); g.MultiSelectTempDataStacked = 0; g.MultiSelectTempData.clear_destruct(); TableGcCompactSettings(); @@ -4565,15 +4626,6 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) // Clear previous active id if (g.ActiveId != 0) { - // While most behaved code would make an effort to not steal active id during window move/drag operations, - // we at least need to be resilient to it. Canceling the move is rather aggressive and users of 'master' branch - // may prefer the weird ill-defined half working situation ('docking' did assert), so may need to rework that. - if (g.MovingWindow != NULL && g.ActiveId == g.MovingWindow->MoveId) - { - IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() cancel MovingWindow\n"); - g.MovingWindow = NULL; - } - // Store deactivate data ImGuiDeactivatedItemData* deactivated_data = &g.DeactivatedItemData; deactivated_data->ID = g.ActiveId; @@ -4586,6 +4638,15 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) // One common scenario leading to this is: pressing Key ->NavMoveRequestApplyResult() -> ClearActiveID() if (g.InputTextState.ID == g.ActiveId) InputTextDeactivateHook(g.ActiveId); + + // While most behaved code would make an effort to not steal active id during window move/drag operations, + // we at least need to be resilient to it. Canceling the move is rather aggressive and users of 'master' branch + // may prefer the weird ill-defined half working situation ('docking' did assert), so may need to rework that. + if (g.MovingWindow != NULL && g.ActiveId == g.MovingWindow->MoveId) + { + IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() cancel MovingWindow\n"); + StopMouseMovingWindow(); + } } // Set active id @@ -4609,6 +4670,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) g.ActiveIdWindow = window; g.ActiveIdHasBeenEditedThisFrame = false; g.ActiveIdFromShortcut = false; + g.ActiveIdDisabledId = 0; if (id) { g.ActiveIdIsAlive = id; @@ -4660,7 +4722,8 @@ void ImGui::MarkItemEdited(ImGuiID id) // 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) - IM_ASSERT(g.DragDropActive || g.ActiveId == id || g.ActiveId == 0 || g.ActiveIdPreviousFrame == id || (g.CurrentMultiSelect != NULL && g.BoxSelectState.IsActive)); + // 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.CurrentWindow->DC.LastItemId == id); g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited; @@ -4752,8 +4815,17 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) const ImGuiID id = g.LastItemData.ID; if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0) if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap) - if (g.ActiveId != window->MoveId) + { + // When ActiveId == MoveId it means that either: + // - (1) user clicked on void _or_ an item with no id, which triggers moving window (ActiveId is set even when window has _NoMove flag) + // - the (id == 0) test handles it, however, IsItemHovered() will leak between id==0 items (mostly visible when using _NoMove). // FIXME: May be fixed. + // - (2) user clicked a disabled item. UpdateMouseMovingWindowEndFrame() uses ActiveId == MoveId to avoid interference with item logic + sets ActiveIdDisabledId. + bool cancel_is_hovered = true; + if (g.ActiveId == window->MoveId && (id == 0 || g.ActiveIdDisabledId == id)) + cancel_is_hovered = false; + if (cancel_is_hovered) return false; + } // Test if interactions on this window are blocked by an active popup or modal. // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here. @@ -4800,7 +4872,8 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) return true; } -// Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered(). +// Internal facing ItemHoverable() used when submitting widgets. THIS IS A SUBMISSION NOT A HOVER CHECK. +// Returns whether the item was hovered, logic differs slightly from IsItemHovered(). // (this does not rely on LastItemData it can be called from a ButtonBehavior() call not following an ItemAdd() call) // FIXME-LEGACY: the 'ImGuiItemFlags item_flags' parameter was added on 2023-06-28. // If you used this in your legacy/custom widgets code: @@ -4812,11 +4885,12 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flag ImGuiWindow* window = g.CurrentWindow; // Detect ID conflicts + // (this is specifically done here by comparing on hover because it allows us a detection of duplicates that is algorithmically extra cheap, 1 u32 compare per item. No O(log N) lookup whatsoever) #ifndef IMGUI_DISABLE_DEBUG_TOOLS if (id != 0 && g.HoveredIdPreviousFrame == id && (item_flags & ImGuiItemFlags_AllowDuplicateId) == 0) { g.HoveredIdPreviousFrameItemCount++; - if (g.DebugDrawIdConflicts == id) + if (g.DebugDrawIdConflictsId == id) 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); } #endif @@ -4832,7 +4906,7 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flag if (!g.ActiveIdFromShortcut) return false; - // Done with rectangle culling so we can perform heavier checks now. + // We are done with rectangle culling so we can perform heavier checks now. if (!(item_flags & ImGuiItemFlags_NoWindowHoverableCheck) && !IsWindowContentHoverable(window, ImGuiHoveredFlags_None)) { g.HoveredIdIsDisabled = true; @@ -4984,7 +5058,7 @@ void ImGui::DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr IM_UNUSED(ptr); if (entry->FrameCount != frame_count) { - info->LastEntriesIdx = (info->LastEntriesIdx + 1) % IM_ARRAYSIZE(info->LastEntriesBuf); + info->LastEntriesIdx = (info->LastEntriesIdx + 1) % IM_COUNTOF(info->LastEntriesBuf); entry = &info->LastEntriesBuf[info->LastEntriesIdx]; entry->FrameCount = frame_count; entry->AllocCount = entry->FreeCount = 0; @@ -5069,7 +5143,7 @@ static ImDrawList* GetViewportBgFgDrawList(ImGuiViewportP* viewport, size_t draw { // Create the draw list on demand, because they are not frequently used for all viewports ImGuiContext& g = *GImGui; - IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->BgFgDrawLists)); + IM_ASSERT(drawlist_no < IM_COUNTOF(viewport->BgFgDrawLists)); ImDrawList* draw_list = viewport->BgFgDrawLists[drawlist_no]; if (draw_list == NULL) { @@ -5137,8 +5211,21 @@ void ImGui::StartMouseMovingWindow(ImGuiWindow* window) g.MovingWindow = window; } +// This is not 100% symetric with StartMouseMovingWindow(). +// We do NOT clear ActiveID, because: +// - It would lead to rather confusing recursive code paths. Caller can call ClearActiveID() if desired. +// - Some code intentionally cancel moving but keep the ActiveID to lock inputs (e.g. code path taken when clicking a disabled item). +void ImGui::StopMouseMovingWindow() +{ + ImGuiContext& g = *GImGui; + + // [nb: docking branch has more stuff in this function] + + g.MovingWindow = NULL; +} + // Handle mouse moving window -// Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing() +// Note: moving window with the navigation keys (Square + d-pad / Ctrl+Tab + Arrows) are processed in NavUpdateWindowing() // FIXME: We don't have strong guarantee that g.MovingWindow stay synced with g.ActiveId == g.MovingWindow->MoveId. // This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs, // but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other. @@ -5160,7 +5247,7 @@ void ImGui::UpdateMouseMovingWindowNewFrame() } else { - g.MovingWindow = NULL; + StopMouseMovingWindow(); ClearActiveID(); } } @@ -5189,31 +5276,39 @@ void ImGui::UpdateMouseMovingWindowEndFrame() if (g.NavWindow && g.NavWindow->Appearing) return; + ImGuiWindow* hovered_window = g.HoveredWindow; + // Click on empty space to focus window and start moving // (after we're done with all our widgets) if (g.IO.MouseClicked[0]) { // 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* root_window = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL; - const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(root_window->PopupId, ImGuiPopupFlags_AnyPopupLevel); + 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); - if (root_window != NULL && !is_closed_popup) + if (hovered_window != NULL && !is_closed_popup) { - StartMouseMovingWindow(g.HoveredWindow); //-V595 + StartMouseMovingWindow(hovered_window); //-V595 + + // FIXME: In principle we might be able to call StopMouseMovingWindow() below. + // Please note how StartMouseMovingWindow() and StopMouseMovingWindow() and not entirely symetrical, at the later doesn't clear ActiveId. // Cancel moving if clicked outside of title bar - if (g.IO.ConfigWindowsMoveFromTitleBarOnly) - if (!(root_window->Flags & ImGuiWindowFlags_NoTitleBar)) - if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0])) + if ((hovered_window->BgClickFlags & ImGuiWindowBgClickFlags_Move) == 0) // set by io.ConfigWindowsMoveFromTitleBarOnly + if (!(hovered_root->Flags & ImGuiWindowFlags_NoTitleBar)) + if (!hovered_root->TitleBarRect().Contains(g.IO.MouseClickedPos[0])) g.MovingWindow = NULL; // Cancel moving if clicked over an item which was disabled or inhibited by popups - // (when g.HoveredIdIsDisabled == true && g.HoveredId == 0 we are inhibited by popups, when g.HoveredIdIsDisabled == true && g.HoveredId != 0 we are over a disabled item)0 already) + // (when g.HoveredIdIsDisabled == true && g.HoveredId == 0 we are inhibited by popups, when g.HoveredIdIsDisabled == true && g.HoveredId != 0 we are over a disabled item) if (g.HoveredIdIsDisabled) + { g.MovingWindow = NULL; + g.ActiveIdDisabledId = g.HoveredId; + } } - else if (root_window == NULL && g.NavWindow != NULL) + else if (hovered_window == NULL && g.NavWindow != NULL) { // Clicking on void disable focus FocusWindow(NULL, ImGuiFocusRequestFlags_UnlessBelowModal); @@ -5228,14 +5323,14 @@ void ImGui::UpdateMouseMovingWindowEndFrame() // Find the top-most window between HoveredWindow and the top-most Modal Window. // This is where we can trim the popup stack. ImGuiWindow* modal = GetTopMostPopupModal(); - bool hovered_window_above_modal = g.HoveredWindow && (modal == NULL || IsWindowAbove(g.HoveredWindow, modal)); - ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true); + bool hovered_window_above_modal = hovered_window && (modal == NULL || IsWindowAbove(hovered_window, modal)); + ClosePopupsOverWindow(hovered_window_above_modal ? hovered_window : modal, true); } } static bool IsWindowActiveAndVisible(ImGuiWindow* window) { - return (window->Active) && (!window->Hidden); + return window->Active && !window->Hidden; } // The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app) @@ -5271,7 +5366,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags(const ImVec2& mouse_pos) const bool has_open_modal = (modal_window != NULL); int mouse_earliest_down = -1; bool mouse_any_down = false; - for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) + for (int i = 0; i < IM_COUNTOF(io.MouseDown); i++) { if (io.MouseClicked[i]) { @@ -5323,46 +5418,6 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags(const ImVec2& mouse_pos) io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false; } -static void ImGui::UpdateTexturesNewFrame() -{ - // Cannot update every atlases based on atlas's FrameCount < g.FrameCount, because an atlas may be shared by multiple contexts with different frame count. - ImGuiContext& g = *GImGui; - const bool has_textures = (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) != 0; - for (ImFontAtlas* atlas : g.FontAtlases) - { - if (atlas->OwnerContext == &g) - { - ImFontAtlasUpdateNewFrame(atlas, g.FrameCount, has_textures); - } - else - { - // (1) If you manage font atlases yourself, e.g. create a ImFontAtlas yourself you need to call ImFontAtlasUpdateNewFrame() on it. - // Otherwise, calling ImGui::CreateContext() without parameter will create an atlas owned by the context. - // (2) If you have multiple font atlases, make sure the 'atlas->RendererHasTextures' as specified in the ImFontAtlasUpdateNewFrame() call matches for that. - // (3) If you have multiple imgui contexts, they also need to have a matching value for ImGuiBackendFlags_RendererHasTextures. - IM_ASSERT(atlas->Builder != NULL && atlas->Builder->FrameCount != -1); - IM_ASSERT(atlas->RendererHasTextures == has_textures); - } - } -} - -// Build a single texture list -static void ImGui::UpdateTexturesEndFrame() -{ - ImGuiContext& g = *GImGui; - g.PlatformIO.Textures.resize(0); - for (ImFontAtlas* atlas : g.FontAtlases) - for (ImTextureData* tex : atlas->TexList) - { - // We provide this information so backends can decide whether to destroy textures. - // This means in practice that if N imgui contexts are created with a shared atlas, we assume all of them have a backend initialized. - tex->RefCount = (unsigned short)atlas->RefCount; - g.PlatformIO.Textures.push_back(tex); - } - for (ImTextureData* tex : g.UserTextures) - g.PlatformIO.Textures.push_back(tex); -} - // Called once a frame. Followed by SetCurrentFont() which sets up the remaining data. // FIXME-VIEWPORT: the concept of a single ClipRectFullscreen is not ideal! static void SetupDrawListSharedData() @@ -5414,8 +5469,8 @@ void ImGui::NewFrame() // Calculate frame-rate for the user, as a purely luxurious feature g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx]; g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime; - g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame); - g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_ARRAYSIZE(g.FramerateSecPerFrame)); + g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_COUNTOF(g.FramerateSecPerFrame); + g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_COUNTOF(g.FramerateSecPerFrame)); g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)g.FramerateSecPerFrameCount)) : FLT_MAX; // Process input queue (trickle as many events as possible), turn events into writes to IO structure @@ -5443,10 +5498,10 @@ void ImGui::NewFrame() KeepAliveID(g.DragDropPayload.SourceId); // [DEBUG] - if (!g.IO.ConfigDebugHighlightIdConflicts || !g.IO.KeyCtrl) // Count is locked while holding CTRL - g.DebugDrawIdConflicts = 0; + if (!g.IO.ConfigDebugHighlightIdConflicts || !g.IO.KeyCtrl) // Count is locked while holding Ctrl + g.DebugDrawIdConflictsId = 0; if (g.IO.ConfigDebugHighlightIdConflicts && g.HoveredIdPreviousFrameItemCount > 1) - g.DebugDrawIdConflicts = g.HoveredIdPreviousFrame; + g.DebugDrawIdConflictsId = g.HoveredIdPreviousFrame; // Update HoveredId data if (!g.HoveredIdPreviousFrame) @@ -5521,15 +5576,6 @@ void ImGui::NewFrame() g.HoverItemDelayTimer = g.HoverItemDelayClearTimer = 0.0f; // May want a decaying timer, in which case need to clamp at max first, based on max of caller last requested timer. } - // Drag and drop - g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr; - g.DragDropAcceptIdCurr = 0; - g.DragDropAcceptIdCurrRectSurface = FLT_MAX; - g.DragDropWithinSource = false; - g.DragDropWithinTarget = false; - g.DragDropHoldJustPressedId = 0; - g.TooltipPreviousWindow = NULL; - // Close popups on focus lost (currently wip/opt-in) //if (g.IO.AppFocusLost) // ClosePopupsExceptModals(); @@ -5542,6 +5588,30 @@ void ImGui::NewFrame() //IM_ASSERT(g.IO.KeyAlt == IsKeyDown(ImGuiKey_LeftAlt) || IsKeyDown(ImGuiKey_RightAlt)); //IM_ASSERT(g.IO.KeySuper == IsKeyDown(ImGuiKey_LeftSuper) || IsKeyDown(ImGuiKey_RightSuper)); + // Drag and drop + g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr; + g.DragDropAcceptIdCurr = 0; + g.DragDropAcceptFlagsPrev = g.DragDropAcceptFlagsCurr; + g.DragDropAcceptFlagsCurr = ImGuiDragDropFlags_None; + g.DragDropAcceptIdCurrRectSurface = FLT_MAX; + g.DragDropWithinSource = false; + g.DragDropWithinTarget = false; + g.DragDropHoldJustPressedId = 0; + if (g.DragDropActive) + { + // Also works when g.ActiveId==0 (aka leftover payload in progress, no active id) + // You may disable this externally by hijacking the input route: + // 'if (GetDragDropPayload() != NULL) { Shortcut(ImGuiKey_Escape, ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverActive); } + // but you will not get a return value from Shortcut() due to ActiveIdUsingAllKeyboardKeys logic. You can however poll IsKeyPressed(ImGuiKey_Escape) afterwards. + ImGuiID owner_id = g.ActiveId ? g.ActiveId : ImHashStr("##DragDropCancelHandler"); + if (Shortcut(ImGuiKey_Escape, ImGuiInputFlags_RouteGlobal, owner_id)) + { + ClearActiveID(); + ClearDragDrop(); + } + } + g.TooltipPreviousWindow = NULL; + // Update keyboard/gamepad navigation NavUpdate(); @@ -5615,7 +5685,7 @@ void ImGui::NewFrame() // [DEBUG] Update debug features #ifndef IMGUI_DISABLE_DEBUG_TOOLS UpdateDebugToolItemPicker(); - UpdateDebugToolStackQueries(); + UpdateDebugToolItemPathQuery(); UpdateDebugToolFlashStyleColor(); if (g.DebugLocateFrames > 0 && --g.DebugLocateFrames == 0) { @@ -5663,7 +5733,7 @@ static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs) return d; if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip)) return d; - return (a->BeginOrderWithinParent - b->BeginOrderWithinParent); + return a->BeginOrderWithinParent - b->BeginOrderWithinParent; } static void AddWindowToSortBuffer(ImVector* out_sorted_windows, ImGuiWindow* window) @@ -5710,10 +5780,10 @@ static void FlattenDrawDataIntoSingleLayer(ImDrawDataBuilder* builder) { int n = builder->Layers[0]->Size; int full_size = n; - for (int i = 1; i < IM_ARRAYSIZE(builder->Layers); i++) + for (int i = 1; i < IM_COUNTOF(builder->Layers); i++) full_size += builder->Layers[i]->Size; builder->Layers[0]->resize(full_size); - for (int layer_n = 1; layer_n < IM_ARRAYSIZE(builder->Layers); layer_n++) + for (int layer_n = 1; layer_n < IM_COUNTOF(builder->Layers); layer_n++) { ImVector* layer = builder->Layers[layer_n]; if (layer->empty()) @@ -5829,10 +5899,10 @@ static void ImGui::RenderDimmedBackgrounds() } else if (dim_bg_for_window_list) { - // Draw dimming behind CTRL+Tab target window and behind CTRL+Tab UI window + // Draw dimming behind Ctrl+Tab target window and behind Ctrl+Tab UI window RenderDimmedBackgroundBehindWindow(g.NavWindowingTargetAnim, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio)); - // Draw border around CTRL+Tab target window + // Draw border around Ctrl+Tab target window ImGuiWindow* window = g.NavWindowingTargetAnim; ImGuiViewport* viewport = GetMainViewport(); float distance = g.FontSize; @@ -5875,9 +5945,9 @@ void ImGui::EndFrame() ImGuiPlatformImeData* ime_data = &g.PlatformImeData; if (g.PlatformIO.Platform_SetImeDataFn != NULL && memcmp(ime_data, &g.PlatformImeDataPrev, sizeof(ImGuiPlatformImeData)) != 0) { - IMGUI_DEBUG_LOG_IO("[io] Calling Platform_SetImeDataFn(): WantVisible: %d, InputPos (%.2f,%.2f)\n", ime_data->WantVisible, ime_data->InputPos.x, ime_data->InputPos.y); IM_ASSERT(ime_data->ViewportId == IMGUI_VIEWPORT_DEFAULT_ID); // master branch ImGuiViewport* viewport = GetMainViewport(); + IMGUI_DEBUG_LOG_IO("[io] Calling Platform_SetImeDataFn(): WantVisible: %d, InputPos (%.2f,%.2f) for Viewport 0x%08X\n", ime_data->WantVisible, ime_data->InputPos.x, ime_data->InputPos.y, viewport->ID); g.PlatformIO.Platform_SetImeDataFn(&g, viewport, ime_data); } g.WantTextInputNextFrame = ime_data->WantTextInput ? 1 : 0; @@ -5888,7 +5958,7 @@ void ImGui::EndFrame() g.CurrentWindow->Active = false; End(); - // Update navigation: CTRL+Tab, wrap-around requests + // Update navigation: Ctrl+Tab, wrap-around requests NavEndFrame(); // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted) @@ -5989,7 +6059,7 @@ void ImGui::Render() if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1]) AddRootWindowToDrawData(window); } - for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++) + for (int n = 0; n < IM_COUNTOF(windows_to_render_top_most); n++) if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window AddRootWindowToDrawData(windows_to_render_top_most[n]); @@ -6139,7 +6209,7 @@ bool ImGui::IsItemDeactivated() ImGuiContext& g = *GImGui; if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDeactivated) return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Deactivated) != 0; - return (g.DeactivatedItemData.ID == g.LastItemData.ID && g.LastItemData.ID != 0 && g.DeactivatedItemData.ElapseFrame >= g.FrameCount); + return g.DeactivatedItemData.ID == g.LastItemData.ID && g.LastItemData.ID != 0 && g.DeactivatedItemData.ElapseFrame >= g.FrameCount; } bool ImGui::IsItemDeactivatedAfterEdit() @@ -6224,16 +6294,16 @@ void ImGui::SetNextItemAllowOverlap() #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority. -// FIXME-LEGACY: Use SetNextItemAllowOverlap() *before* your item instead. -void ImGui::SetItemAllowOverlap() -{ - ImGuiContext& g = *GImGui; - ImGuiID id = g.LastItemData.ID; - if (g.HoveredId == id) - g.HoveredIdAllowOverlap = true; - if (g.ActiveId == id) // Before we made this obsolete, most calls to SetItemAllowOverlap() used to avoid this path by testing g.ActiveId != id. - g.ActiveIdAllowOverlap = true; -} +// Use SetNextItemAllowOverlap() *before* your item instead of calling this! +//void ImGui::SetItemAllowOverlap() +//{ +// ImGuiContext& g = *GImGui; +// ImGuiID id = g.LastItemData.ID; +// if (g.HoveredId == id) +// g.HoveredIdAllowOverlap = true; +// if (g.ActiveId == id) // Before we made this obsolete, most calls to SetItemAllowOverlap() used to avoid this path by testing g.ActiveId != id. +// g.ActiveIdAllowOverlap = true; +//} #endif // This is a shortcut for not taking ownership of 100+ keys, frequently used by drag operations. @@ -6271,6 +6341,12 @@ ImVec2 ImGui::GetItemRectSize() return g.LastItemData.Rect.GetSize(); } +ImGuiItemFlags ImGui::GetItemFlags() +{ + ImGuiContext& g = *GImGui; + return g.LastItemData.ItemFlags; +} + // Prior to v1.90 2023/10/16, the BeginChild() function took a 'bool border = false' parameter instead of 'ImGuiChildFlags child_flags = 0'. // ImGuiChildFlags_Borders is defined as always == 1 in order to allow old code passing 'true'. Read comments in imgui.h for details! bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags) @@ -6301,10 +6377,10 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, I IM_ASSERT((child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY)) != 0 && "Must use ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY with ImGuiChildFlags_AlwaysAutoResize!"); } #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - if (window_flags & ImGuiWindowFlags_AlwaysUseWindowPadding) - child_flags |= ImGuiChildFlags_AlwaysUseWindowPadding; - if (window_flags & ImGuiWindowFlags_NavFlattened) - child_flags |= ImGuiChildFlags_NavFlattened; + //if (window_flags & ImGuiWindowFlags_AlwaysUseWindowPadding) + // child_flags |= ImGuiChildFlags_AlwaysUseWindowPadding; + //if (window_flags & ImGuiWindowFlags_NavFlattened) + // child_flags |= ImGuiChildFlags_NavFlattened; #endif if (child_flags & ImGuiChildFlags_AutoResizeX) child_flags &= ~ImGuiChildFlags_ResizeX; @@ -6555,13 +6631,13 @@ static inline ImVec2 CalcWindowMinSize(ImGuiWindow* window) ImVec2 size_min; if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_Popup)) { - size_min.x = (window->ChildFlags & ImGuiChildFlags_ResizeX) ? g.Style.WindowMinSize.x : 4.0f; - size_min.y = (window->ChildFlags & ImGuiChildFlags_ResizeY) ? g.Style.WindowMinSize.y : 4.0f; + size_min.x = (window->ChildFlags & ImGuiChildFlags_ResizeX) ? g.Style.WindowMinSize.x : IMGUI_WINDOW_HARD_MIN_SIZE; + size_min.y = (window->ChildFlags & ImGuiChildFlags_ResizeY) ? g.Style.WindowMinSize.y : IMGUI_WINDOW_HARD_MIN_SIZE; } else { - size_min.x = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.x : 4.0f; - size_min.y = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.y : 4.0f; + size_min.x = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.x : IMGUI_WINDOW_HARD_MIN_SIZE; + size_min.y = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.y : IMGUI_WINDOW_HARD_MIN_SIZE; } // Reduce artifacts with very small windows @@ -6619,14 +6695,16 @@ static void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_cur content_size_ideal->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : ImTrunc64(ImMax(window->DC.CursorMaxPos.y, window->DC.IdealMaxPos.y) - window->DC.CursorStartPos.y); } -static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents) +static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents, int axis_mask) { ImGuiContext& g = *GImGui; ImGuiStyle& style = g.Style; const float decoration_w_without_scrollbars = window->DecoOuterSizeX1 + window->DecoOuterSizeX2 - window->ScrollbarSizes.x; const float decoration_h_without_scrollbars = window->DecoOuterSizeY1 + window->DecoOuterSizeY2 - window->ScrollbarSizes.y; ImVec2 size_pad = window->WindowPadding * 2.0f; - ImVec2 size_desired = size_contents + size_pad + ImVec2(decoration_w_without_scrollbars, decoration_h_without_scrollbars); + ImVec2 size_desired; + size_desired[ImGuiAxis_X] = (axis_mask & 1) ? size_contents.x + size_pad.x + decoration_w_without_scrollbars : window->Size.x; + size_desired[ImGuiAxis_Y] = (axis_mask & 2) ? size_contents.y + size_pad.y + decoration_h_without_scrollbars : window->Size.y; // Determine maximum window size // Child windows are layed within their parent (unless they are also popups/menus) and thus have no restriction @@ -6642,14 +6720,6 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont ImVec2 size_min = CalcWindowMinSize(window); ImVec2 size_auto_fit = ImClamp(size_desired, ImMin(size_min, size_max), size_max); - // FIXME: CalcWindowAutoFitSize() doesn't take into account that only one axis may be auto-fit when calculating scrollbars, - // we may need to compute/store three variants of size_auto_fit, for x/y/xy. - // Here we implement a workaround for child windows only, but a full solution would apply to normal windows as well: - if ((window->ChildFlags & ImGuiChildFlags_ResizeX) && !(window->ChildFlags & (ImGuiChildFlags_ResizeY | ImGuiChildFlags_AutoResizeY))) - size_auto_fit.y = window->SizeFull.y; - else if ((window->ChildFlags & ImGuiChildFlags_ResizeY) && !(window->ChildFlags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_AutoResizeX))) - size_auto_fit.x = window->SizeFull.x; - // When the window cannot fit all contents (either because of constraints, either because screen is too small), // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding. ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit); @@ -6668,7 +6738,7 @@ ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window) ImVec2 size_contents_current; ImVec2 size_contents_ideal; CalcWindowContentSizes(window, &size_contents_current, &size_contents_ideal); - ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents_ideal); + ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents_ideal, ~0); ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit); return size_final; } @@ -6682,8 +6752,20 @@ static ImGuiCol GetWindowBgColorIdx(ImGuiWindow* window) return ImGuiCol_WindowBg; } -static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size) +static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target_arg, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size) { + ImVec2 corner_target = corner_target_arg; + if (window->Flags & ImGuiWindowFlags_ChildWindow) // Clamp resizing of childs within parent + { + ImGuiWindow* parent_window = window->ParentWindow; + ImGuiWindowFlags parent_flags = parent_window->Flags; + ImRect limit_rect = parent_window->InnerRect; + limit_rect.Expand(ImVec2(-ImMax(parent_window->WindowPadding.x, parent_window->WindowBorderSize), -ImMax(parent_window->WindowPadding.y, parent_window->WindowBorderSize))); + if ((parent_flags & (ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar)) == 0 || (parent_flags & ImGuiWindowFlags_NoScrollbar)) + corner_target.x = ImClamp(corner_target.x, limit_rect.Min.x, limit_rect.Max.x); + if (parent_flags & ImGuiWindowFlags_NoScrollbar) + corner_target.y = ImClamp(corner_target.y, limit_rect.Min.y, limit_rect.Max.y); + } ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right ImVec2 size_expected = pos_max - pos_min; @@ -6762,7 +6844,7 @@ ImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir) // Handle resize for: Resize Grips, Borders, Gamepad // Return true when using auto-fit (double-click on resize grip) -static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect) +static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect) { ImGuiContext& g = *GImGui; ImGuiWindowFlags flags = window->Flags; @@ -6780,7 +6862,7 @@ static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& si const float grip_hover_outer_size = g.WindowsBorderHoverPadding; ImRect clamp_rect = visibility_rect; - const bool window_move_from_title_bar = g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar); + const bool window_move_from_title_bar = !(window->BgClickFlags & ImGuiWindowBgClickFlags_Move) && !(window->Flags & ImGuiWindowFlags_NoTitleBar); if (window_move_from_title_bar) clamp_rect.Min.y -= window->TitleBarHeight; @@ -6812,6 +6894,7 @@ static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& si if (held && g.IO.MouseDoubleClicked[0]) { // Auto-fit when double-clicking + ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal, ~0); size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit); ret_auto_fit_mask = 0x03; // Both axes ClearActiveID(); @@ -6828,7 +6911,8 @@ static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& si } // Only lower-left grip is visible before hovering/activating - if (resize_grip_n == 0 || held || hovered) + const bool resize_grip_visible = held || hovered || (resize_grip_n == 0 && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0); + if (resize_grip_visible) resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); } @@ -6857,10 +6941,10 @@ static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& si if (held && g.IO.MouseDoubleClicked[0]) { // Double-clicking bottom or right border auto-fit on this axis - // FIXME: CalcWindowAutoFitSize() doesn't take into account that only one side may be auto-fit when calculating scrollbars. // FIXME: Support top and right borders: rework CalcResizePosSizeFromAnyCorner() to be reusable in both cases. if (border_n == 1 || border_n == 3) // Right and bottom border { + ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal, 1 << axis); size_target[axis] = CalcWindowSizeAfterConstraint(window, size_auto_fit)[axis]; ret_auto_fit_mask |= (1 << axis); hovered = held = false; // So border doesn't show highlighted at new position @@ -6903,17 +6987,6 @@ static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& si ImVec2 clamp_min(border_n == ImGuiDir_Right ? clamp_rect.Min.x : -FLT_MAX, border_n == ImGuiDir_Down || (border_n == ImGuiDir_Up && window_move_from_title_bar) ? clamp_rect.Min.y : -FLT_MAX); ImVec2 clamp_max(border_n == ImGuiDir_Left ? clamp_rect.Max.x : +FLT_MAX, border_n == ImGuiDir_Up ? clamp_rect.Max.y : +FLT_MAX); border_target = ImClamp(border_target, clamp_min, clamp_max); - if (flags & ImGuiWindowFlags_ChildWindow) // Clamp resizing of childs within parent - { - ImGuiWindow* parent_window = window->ParentWindow; - ImGuiWindowFlags parent_flags = parent_window->Flags; - ImRect border_limit_rect = parent_window->InnerRect; - border_limit_rect.Expand(ImVec2(-ImMax(parent_window->WindowPadding.x, parent_window->WindowBorderSize), -ImMax(parent_window->WindowPadding.y, parent_window->WindowBorderSize))); - if ((axis == ImGuiAxis_X) && ((parent_flags & (ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar)) == 0 || (parent_flags & ImGuiWindowFlags_NoScrollbar))) - border_target.x = ImClamp(border_target.x, border_limit_rect.Min.x, border_limit_rect.Max.x); - if ((axis == ImGuiAxis_Y) && (parent_flags & ImGuiWindowFlags_NoScrollbar)) - border_target.y = ImClamp(border_target.y, border_limit_rect.Min.y, border_limit_rect.Max.y); - } if (!ignore_resize) CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target); } @@ -6957,8 +7030,8 @@ static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& si } // Apply back modified position/size to window - const ImVec2 curr_pos = window->Pos; - const ImVec2 curr_size = window->SizeFull; + const ImVec2 old_pos = window->Pos; + const ImVec2 old_size = window->SizeFull; if (size_target.x != FLT_MAX && (window->Size.x != size_target.x || window->SizeFull.x != size_target.x)) window->Size.x = window->SizeFull.x = size_target.x; if (size_target.y != FLT_MAX && (window->Size.y != size_target.y || window->SizeFull.y != size_target.y)) @@ -6967,7 +7040,7 @@ static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& si window->Pos.x = ImTrunc(pos_target.x); if (pos_target.y != FLT_MAX && window->Pos.y != ImTrunc(pos_target.y)) window->Pos.y = ImTrunc(pos_target.y); - if (curr_pos.x != window->Pos.x || curr_pos.y != window->Pos.y || curr_size.x != window->SizeFull.x || curr_size.y != window->SizeFull.y) + if (old_pos.x != window->Pos.x || old_pos.y != window->Pos.y || old_size.x != window->SizeFull.x || old_size.y != window->SizeFull.y) MarkIniSettingsDirty(window); // Recalculate next expected border expected coordinates @@ -6979,9 +7052,8 @@ static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& si static inline void ClampWindowPos(ImGuiWindow* window, const ImRect& visibility_rect) { - ImGuiContext& g = *GImGui; ImVec2 size_for_clamping = window->Size; - if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) + if (!(window->BgClickFlags & ImGuiWindowBgClickFlags_Move) && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) size_for_clamping.y = window->TitleBarHeight; window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max); } @@ -7162,10 +7234,11 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl // Close button if (has_close_button) { + ImGuiItemFlags backup_item_flags = g.CurrentItemFlags; g.CurrentItemFlags |= ImGuiItemFlags_NoFocus; if (CloseButton(window->GetID("#CLOSE"), close_button_pos)) *p_open = false; - g.CurrentItemFlags &= ~ImGuiItemFlags_NoFocus; + g.CurrentItemFlags = backup_item_flags; } window->DC.NavLayerCurrent = ImGuiNavLayer_Main; @@ -7199,7 +7272,7 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl marker_pos.y = (layout_r.Min.y + layout_r.Max.y) * 0.5f; if (marker_pos.x > layout_r.Min.x) { - RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_Text)); + RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_UnsavedMarker)); clip_r.Max.x = ImMin(clip_r.Max.x, marker_pos.x - (int)(marker_size_x * 0.5f)); } } @@ -7216,7 +7289,7 @@ void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags window->RootWindow = parent_window->RootWindow; if (parent_window && (flags & ImGuiWindowFlags_Popup)) window->RootWindowPopupTree = parent_window->RootWindowPopupTree; - if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) + if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip))) window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight; while (window->RootWindowForNav->ChildFlags & ImGuiChildFlags_NavFlattened) { @@ -7325,7 +7398,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window; - ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow; + ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) ? parent_window_in_stack : NULL) : window->ParentWindow; IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); // We allow window memory to be compacted so recreate the base stack when needed. @@ -7374,7 +7447,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } // Process SetNextWindow***() calls - // (FIXME: Consider splitting the HasXXX flags into X/Y components + // (FIXME: Consider splitting the HasXXX flags into X/Y components) bool window_pos_set_by_api = false; bool window_size_x_set_by_api = false, window_size_y_set_by_api = false; if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasPos) @@ -7457,11 +7530,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged). // The title bar always display the 'name' parameter, so we only update the string storage if it needs to be visible to the end-user elsewhere. bool window_title_visible_elsewhere = false; - if (g.NavWindowingListWindow != NULL && (flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB + if (g.NavWindowingListWindow != NULL && g.NavWindowingListWindow->WasActive && (flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using Ctrl+Tab window_title_visible_elsewhere = true; if (flags & ImGuiWindowFlags_ChildMenu) window_title_visible_elsewhere = true; - if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0) + if ((window_title_visible_elsewhere || window_just_activated_by_user) && !window_just_created && strcmp(name, window->Name) != 0) { size_t buf_len = (size_t)window->NameBufLen; window->Name = ImStrdupcpy(window->Name, &buf_len, name); @@ -7568,36 +7641,39 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->ScrollbarSizes = ImVec2(0.0f, 0.0f); // Calculate auto-fit size, handle automatic resize - const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal); - if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed) - { - // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc. - if (!window_size_x_set_by_api) - { - window->SizeFull.x = size_auto_fit.x; - use_current_size_for_scrollbar_x = true; - } - if (!window_size_y_set_by_api) - { - window->SizeFull.y = size_auto_fit.y; - use_current_size_for_scrollbar_y = true; - } - } - else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) + // - Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc. + // - We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed. + // - Auto-fit may only grow window during the first few frames. { - // Auto-fit may only grow window during the first few frames - // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed. - if (!window_size_x_set_by_api && window->AutoFitFramesX > 0) + const bool size_auto_fit_x_always = !window_size_x_set_by_api && (flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed; + const bool size_auto_fit_y_always = !window_size_y_set_by_api && (flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed; + const bool size_auto_fit_x_current = !window_size_x_set_by_api && (window->AutoFitFramesX > 0); + const bool size_auto_fit_y_current = !window_size_y_set_by_api && (window->AutoFitFramesY > 0); + int size_auto_fit_mask = 0; + if (size_auto_fit_x_always || size_auto_fit_x_current) + size_auto_fit_mask |= (1 << ImGuiAxis_X); + if (size_auto_fit_y_always || size_auto_fit_y_current) + size_auto_fit_mask |= (1 << ImGuiAxis_Y); + const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal, size_auto_fit_mask); + + const ImVec2 old_size = window->SizeFull; + if (size_auto_fit_x_always || size_auto_fit_x_current) { - window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x; + if (size_auto_fit_x_always) + window->SizeFull.x = size_auto_fit.x; + else + window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x; use_current_size_for_scrollbar_x = true; } - if (!window_size_y_set_by_api && window->AutoFitFramesY > 0) + if (size_auto_fit_y_always || size_auto_fit_y_current) { - window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y; + if (size_auto_fit_y_always) + window->SizeFull.y = size_auto_fit.y; + else + window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y; use_current_size_for_scrollbar_y = true; } - if (!window->Collapsed) + if (old_size.x != window->SizeFull.x || old_size.y != window->SizeFull.y) MarkIniSettingsDirty(window); } @@ -7682,13 +7758,26 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } #endif + // Decide if we are going to handle borders and resize grips + // 'window->SkipItems' is not updated yet so for child windows we rely on ParentWindow to avoid submitting decorations. (#8815) + // Whenever we add support for full decorated child windows we will likely make this logic more general. + bool handle_borders_and_resize_grips = true; + if ((flags & ImGuiWindowFlags_ChildWindow) && window->ParentWindow->SkipItems) + handle_borders_and_resize_grips = false; + // Handle manual resize: Resize Grips, Borders, Gamepad + // Child windows can only be resized when they have the flags set. The resize grip allows resizing in both directions, so it should appear only if both flags are set. int border_hovered = -1, border_held = -1; ImU32 resize_grip_col[4] = {}; - const int resize_grip_count = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup)) ? 0 : g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it. + int resize_grip_count; + if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup)) + resize_grip_count = ((window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->ChildFlags & ImGuiChildFlags_ResizeY)) ? 1 : 0; + else + resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it. + const float resize_grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f)); - if (!window->Collapsed) - if (int auto_fit_mask = UpdateWindowManualResize(window, size_auto_fit, &border_hovered, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect)) + if (handle_borders_and_resize_grips && !window->Collapsed) + if (int auto_fit_mask = UpdateWindowManualResize(window, &border_hovered, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect)) { if (auto_fit_mask & (1 << ImGuiAxis_X)) use_current_size_for_scrollbar_x = true; @@ -7800,7 +7889,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Setup draw list and outer clipping rectangle IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0); - window->DrawList->PushTexture(g.Font->ContainerAtlas->TexRef); + window->DrawList->PushTexture(g.Font->OwnerAtlas->TexRef); PushClipRect(host_rect.Min, host_rect.Max, false); // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71) @@ -7824,7 +7913,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Handle title bar, scrollbar, resize grips and resize borders const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow; const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight); - const bool handle_borders_and_resize_grips = true; // This exists to facilitate merge with 'docking' branch. RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, handle_borders_and_resize_grips, resize_grip_count, resize_grip_col, resize_grip_draw_size); if (render_decorations_in_parent) @@ -7921,7 +8009,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (want_focus && window == g.NavWindow) NavInitWindow(window, false); // <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls - // Pressing CTRL+C copy window content into the clipboard + // Pressing Ctrl+C copy window content into the clipboard // [EXPERIMENTAL] Breaks on nested Begin/End pairs. We need to work that out and add better logging scope. // [EXPERIMENTAL] Text outputs has many issues. if (g.IO.ConfigWindowsCopyContentsWithCtrlC) @@ -7938,6 +8026,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (flags & ImGuiWindowFlags_Tooltip) g.TooltipPreviousWindow = window; + // Set default BgClickFlags + // This is set at the end of this function, so UpdateWindowManualResize()/ClampWindowPos() may use last-frame value if overriden by user code. + // FIXME: The general intent is that we will later expose config options to default to enable scrolling + select scrolling mouse button. + window->BgClickFlags = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->BgClickFlags : (g.IO.ConfigWindowsMoveFromTitleBarOnly ? ImGuiWindowBgClickFlags_None : ImGuiWindowBgClickFlags_Move); + // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin(). // This is useful to allow creating context menus on title bar only, etc. window->DC.WindowItemStatusFlags = ImGuiItemStatusFlags_None; @@ -8189,12 +8282,13 @@ void ImGui::EndDisabledOverrideReenable() g.Style.Alpha = g.CurrentWindowStack.back().DisabledOverrideReenableAlphaBackup; } -void ImGui::PushTextWrapPos(float wrap_pos_x) +// ATTENTION THIS IS IN LEGACY LOCAL SPACE. +void ImGui::PushTextWrapPos(float wrap_local_pos_x) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; window->DC.TextWrapPosStack.push_back(window->DC.TextWrapPos); - window->DC.TextWrapPos = wrap_pos_x; + window->DC.TextWrapPos = wrap_local_pos_x; } void ImGui::PopTextWrapPos() @@ -8239,6 +8333,15 @@ bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, return false; } +bool ImGui::IsWindowInBeginStack(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + for (int n = g.CurrentWindowStack.Size - 1; n >= 0; n--) + if (g.CurrentWindowStack[n].Window == window) + return true; + return false; +} + bool ImGui::IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent) { if (window->RootWindow == potential_parent) @@ -8503,7 +8606,7 @@ void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& s } // Content size = inner scrollable rectangle, padded with WindowPadding. -// SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item. +// SetNextWindowContentSize(ImVec2(100,100)) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item. void ImGui::SetNextWindowContentSize(const ImVec2& size) { ImGuiContext& g = *GImGui; @@ -8740,11 +8843,13 @@ bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) } //----------------------------------------------------------------------------- -// [SECTION] FONTS +// [SECTION] FONTS, TEXTURES //----------------------------------------------------------------------------- // Most of the relevant font logic is in imgui_draw.cpp. // Those are high-level support functions. //----------------------------------------------------------------------------- +// - UpdateTexturesNewFrame() [Internal] +// - UpdateTexturesEndFrame() [Internal] // - UpdateFontsNewFrame() [Internal] // - UpdateFontsEndFrame() [Internal] // - GetDefaultFont() [Internal] @@ -8759,6 +8864,46 @@ bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) // - PopFont() //----------------------------------------------------------------------------- +static void ImGui::UpdateTexturesNewFrame() +{ + // Cannot update every atlases based on atlas's FrameCount < g.FrameCount, because an atlas may be shared by multiple contexts with different frame count. + ImGuiContext& g = *GImGui; + const bool has_textures = (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) != 0; + for (ImFontAtlas* atlas : g.FontAtlases) + { + if (atlas->OwnerContext == &g) + { + ImFontAtlasUpdateNewFrame(atlas, g.FrameCount, has_textures); + } + else + { + // (1) If you manage font atlases yourself, e.g. create a ImFontAtlas yourself you need to call ImFontAtlasUpdateNewFrame() on it. + // Otherwise, calling ImGui::CreateContext() without parameter will create an atlas owned by the context. + // (2) If you have multiple font atlases, make sure the 'atlas->RendererHasTextures' as specified in the ImFontAtlasUpdateNewFrame() call matches for that. + // (3) If you have multiple imgui contexts, they also need to have a matching value for ImGuiBackendFlags_RendererHasTextures. + IM_ASSERT(atlas->Builder != NULL && atlas->Builder->FrameCount != -1); + IM_ASSERT(atlas->RendererHasTextures == has_textures); + } + } +} + +// Build a single texture list +static void ImGui::UpdateTexturesEndFrame() +{ + ImGuiContext& g = *GImGui; + g.PlatformIO.Textures.resize(0); + for (ImFontAtlas* atlas : g.FontAtlases) + for (ImTextureData* tex : atlas->TexList) + { + // We provide this information so backends can decide whether to destroy textures. + // This means in practice that if N imgui contexts are created with a shared atlas, we assume all of them have a backend initialized. + tex->RefCount = (unsigned short)atlas->RefCount; + g.PlatformIO.Textures.push_back(tex); + } + for (ImTextureData* tex : g.UserTextures) + g.PlatformIO.Textures.push_back(tex); +} + void ImGui::UpdateFontsNewFrame() { ImGuiContext& g = *GImGui; @@ -8775,7 +8920,7 @@ void ImGui::UpdateFontsNewFrame() // Apply default font size the first time ImFont* font = ImGui::GetDefaultFont(); if (g.Style.FontSizeBase <= 0.0f) - g.Style.FontSizeBase = (font->LegacySize > 0.0f ? font->LegacySize : FONT_DEFAULT_SIZE); + g.Style.FontSizeBase = (font->LegacySize > 0.0f ? font->LegacySize : FONT_DEFAULT_SIZE_BASE); // Set initial font g.Font = font; @@ -8801,16 +8946,19 @@ ImFont* ImGui::GetDefaultFont() return g.IO.FontDefault ? g.IO.FontDefault : atlas->Fonts[0]; } +// EXPERIMENTAL: DO NOT USE YET. void ImGui::RegisterUserTexture(ImTextureData* tex) { ImGuiContext& g = *GImGui; - IM_ASSERT(tex->RefCount > 0); + tex->RefCount++; g.UserTextures.push_back(tex); } void ImGui::UnregisterUserTexture(ImTextureData* tex) { ImGuiContext& g = *GImGui; + IM_ASSERT(tex->RefCount > 0); + tex->RefCount--; g.UserTextures.find_erase(tex); } @@ -8822,6 +8970,8 @@ void ImGui::RegisterFontAtlas(ImFontAtlas* atlas) atlas->RefCount++; g.FontAtlases.push_back(atlas); ImFontAtlasAddDrawListSharedData(atlas, &g.DrawListSharedData); + for (ImTextureData* tex : atlas->TexList) + tex->RefCount = (unsigned short)atlas->RefCount; } void ImGui::UnregisterFontAtlas(ImFontAtlas* atlas) @@ -8831,6 +8981,8 @@ void ImGui::UnregisterFontAtlas(ImFontAtlas* atlas) ImFontAtlasRemoveDrawListSharedData(atlas, &g.DrawListSharedData); g.FontAtlases.find_erase(atlas); atlas->RefCount--; + for (ImTextureData* tex : atlas->TexList) + tex->RefCount = (unsigned short)atlas->RefCount; } // Use ImDrawList::_SetTexture(), making our shared g.FontStack[] authoritative against window-local ImDrawList. @@ -8856,7 +9008,7 @@ void ImGui::SetCurrentFont(ImFont* font, float font_size_before_scaling, float f #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS IM_ASSERT(font->Scale > 0.0f); #endif - ImFontAtlas* atlas = font->ContainerAtlas; + ImFontAtlas* atlas = font->OwnerAtlas; g.DrawListSharedData.FontAtlas = atlas; g.DrawListSharedData.Font = font; ImFontAtlasUpdateDrawListsSharedData(atlas); @@ -8873,11 +9025,14 @@ void ImGui::UpdateCurrentFontSize(float restore_font_size_after_scaling) g.Style.FontSizeBase = g.FontSizeBase; // Early out to avoid hidden window keeping bakes referenced and out of GC reach. - // However this would leave a pretty subtle and damning error surface area if g.FontBaked was mismatching, so for now we null it. + // However this would leave a pretty subtle and damning error surface area if g.FontBaked was mismatching. // FIXME: perhaps g.FontSize should be updated? if (window != NULL && window->SkipItems) - if (g.CurrentTable == NULL || g.CurrentTable->CurrentColumn != -1) // See 8465#issuecomment-2951509561. Ideally the SkipItems=true in tables would be amended with extra data. + { + ImGuiTable* table = g.CurrentTable; + if (table == NULL || (table->CurrentColumn != -1 && table->Columns[table->CurrentColumn].IsSkipItems == false)) // See 8465#issuecomment-2951509561 and #8865. Ideally the SkipItems=true in tables would be amended with extra data. return; + } // Restoring is pretty much only used by PopFont() float final_size = (restore_font_size_after_scaling > 0.0f) ? restore_font_size_after_scaling : 0.0f; @@ -8905,7 +9060,7 @@ void ImGui::UpdateCurrentFontSize(float restore_font_size_after_scaling) // - We started rounding in 1.90 WIP (18991) as our layout system currently doesn't support non-rounded font size well yet. // - We may support it better later and remove this rounding. final_size = GetRoundedFontSize(final_size); - final_size = ImMax(1.0f, final_size); + final_size = ImClamp(final_size, 1.0f, IMGUI_FONT_SIZE_MAX); if (g.Font != NULL && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures)) g.Font->CurrentRasterizerDensity = g.FontRasterizerDensity; g.FontSize = final_size; @@ -8970,7 +9125,7 @@ ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); #ifndef IMGUI_DISABLE_DEBUG_TOOLS ImGuiContext& g = *Ctx; - if (g.DebugHookIdInfo == id) + if (g.DebugHookIdInfoId == id) ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); #endif return id; @@ -8982,7 +9137,7 @@ ImGuiID ImGuiWindow::GetID(const void* ptr) ImGuiID id = ImHashData(&ptr, sizeof(void*), seed); #ifndef IMGUI_DISABLE_DEBUG_TOOLS ImGuiContext& g = *Ctx; - if (g.DebugHookIdInfo == id) + if (g.DebugHookIdInfoId == id) ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL); #endif return id; @@ -8994,7 +9149,7 @@ ImGuiID ImGuiWindow::GetID(int n) ImGuiID id = ImHashData(&n, sizeof(n), seed); #ifndef IMGUI_DISABLE_DEBUG_TOOLS ImGuiContext& g = *Ctx; - if (g.DebugHookIdInfo == id) + if (g.DebugHookIdInfoId == id) ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL); #endif return id; @@ -9057,7 +9212,7 @@ void ImGui::PushOverrideID(ImGuiID id) ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; #ifndef IMGUI_DISABLE_DEBUG_TOOLS - if (g.DebugHookIdInfo == id) + if (g.DebugHookIdInfoId == id) DebugHookIdInfo(id, ImGuiDataType_ID, NULL, NULL); #endif window->IDStack.push_back(id); @@ -9071,7 +9226,7 @@ ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed) ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); #ifndef IMGUI_DISABLE_DEBUG_TOOLS ImGuiContext& g = *GImGui; - if (g.DebugHookIdInfo == id) + if (g.DebugHookIdInfoId == id) DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); #endif return id; @@ -9082,7 +9237,7 @@ ImGuiID ImGui::GetIDWithSeed(int n, ImGuiID seed) ImGuiID id = ImHashData(&n, sizeof(n), seed); #ifndef IMGUI_DISABLE_DEBUG_TOOLS ImGuiContext& g = *GImGui; - if (g.DebugHookIdInfo == id) + if (g.DebugHookIdInfoId == id) DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL); #endif return id; @@ -9249,7 +9404,7 @@ static const char* const GKeyNames[] = "MouseLeft", "MouseRight", "MouseMiddle", "MouseX1", "MouseX2", "MouseWheelX", "MouseWheelY", "ModCtrl", "ModShift", "ModAlt", "ModSuper", // ReservedForModXXX are showing the ModXXX names. }; -IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_ARRAYSIZE(GKeyNames)); +IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_COUNTOF(GKeyNames)); const char* ImGui::GetKeyName(ImGuiKey key) { @@ -9273,7 +9428,7 @@ const char* ImGui::GetKeyChordName(ImGuiKeyChord key_chord) const ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); if (IsLRModKey(key)) key_chord &= ~GetModForLRModKey(key); // Return "Ctrl+LeftShift" instead of "Ctrl+Shift+LeftShift" - ImFormatString(g.TempKeychordName, IM_ARRAYSIZE(g.TempKeychordName), "%s%s%s%s%s", + ImFormatString(g.TempKeychordName, IM_COUNTOF(g.TempKeychordName), "%s%s%s%s%s", (key_chord & ImGuiMod_Ctrl) ? "Ctrl+" : "", (key_chord & ImGuiMod_Shift) ? "Shift+" : "", (key_chord & ImGuiMod_Alt) ? "Alt+" : "", @@ -9297,7 +9452,7 @@ int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, flo if (t0 >= t1) return 0; if (repeat_rate <= 0.0f) - return (t0 < repeat_delay) && (t1 >= repeat_delay); + return t0 < repeat_delay && t1 >= repeat_delay; const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate); const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate); const int count = count_t1 - count_t0; @@ -9353,7 +9508,7 @@ static void ImGui::UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt) routing_entry->RoutingCurrScore = routing_entry->RoutingNextScore; routing_entry->RoutingCurr = routing_entry->RoutingNext; // Update entry routing_entry->RoutingNext = ImGuiKeyOwner_NoOwner; - routing_entry->RoutingNextScore = 255; + routing_entry->RoutingNextScore = 0; if (routing_entry->RoutingCurr == ImGuiKeyOwner_NoOwner) continue; rt->EntriesNext.push_back(*routing_entry); // Write alive ones into new buffer @@ -9422,23 +9577,24 @@ ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord) return routing_data; } -// Current score encoding (lower is highest priority): -// - 0: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverActive -// - 1: ImGuiInputFlags_ActiveItem or ImGuiInputFlags_RouteFocused (if item active) -// - 2: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused -// - 3+: ImGuiInputFlags_RouteFocused (if window in focus-stack) -// - 254: ImGuiInputFlags_RouteGlobal -// - 255: never route +// Current score encoding +// - 0: Never route +// - 1: ImGuiInputFlags_RouteGlobal (lower priority) +// - 100..199: ImGuiInputFlags_RouteFocused (if window in focus-stack) +// 200: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused +// 300: ImGuiInputFlags_RouteActive or ImGuiInputFlags_RouteFocused (if item active) +// 400: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverActive +// - 500..599: ImGuiInputFlags_RouteFocused | ImGuiInputFlags_RouteOverActive (if window in focus-stack) (higher priority) // 'flags' should include an explicit routing policy static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInputFlags flags) { ImGuiContext& g = *GImGui; if (flags & ImGuiInputFlags_RouteFocused) { - // ActiveID gets top priority + // ActiveID gets high priority // (we don't check g.ActiveIdUsingAllKeys here. Routing is applied but if input ownership is tested later it may discard it) if (owner_id != 0 && g.ActiveId == owner_id) - return 1; + return 300; // Score based on distance to focused window (lower is better) // Assuming both windows are submitting a routing request, @@ -9448,25 +9604,32 @@ static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInput // - When Window/ChildB is focused -> Window scores 4 (best), Window/ChildB doesn't have a score. // This essentially follow the window->ParentWindowForFocusRoute chain. if (focus_scope_id == 0) - return 255; + return 0; for (int index_in_focus_path = 0; index_in_focus_path < g.NavFocusRoute.Size; index_in_focus_path++) if (g.NavFocusRoute.Data[index_in_focus_path].ID == focus_scope_id) - return 3 + index_in_focus_path; - return 255; + { + if (flags & ImGuiInputFlags_RouteOverActive) // && g.ActiveId != 0 && g.ActiveId != owner_id) + return 599 - index_in_focus_path; + else + return 199 - index_in_focus_path; + } + return 0; } else if (flags & ImGuiInputFlags_RouteActive) { if (owner_id != 0 && g.ActiveId == owner_id) - return 1; - return 255; + return 300; + return 0; } else if (flags & ImGuiInputFlags_RouteGlobal) { if (flags & ImGuiInputFlags_RouteOverActive) - return 0; + return 400; + if (owner_id != 0 && g.ActiveId == owner_id) + return 300; if (flags & ImGuiInputFlags_RouteOverFocused) - return 2; - return 254; + return 200; + return 1; } IM_ASSERT(0); return 0; @@ -9506,8 +9669,10 @@ bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, I else IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteTypeMask_)); // Check that only 1 routing flag is used IM_ASSERT(owner_id != ImGuiKeyOwner_Any && owner_id != ImGuiKeyOwner_NoOwner); - if (flags & (ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused)) + if (flags & (ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteUnlessBgFocused)) IM_ASSERT(flags & ImGuiInputFlags_RouteGlobal); + if (flags & ImGuiInputFlags_RouteOverActive) + IM_ASSERT(flags & (ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteFocused)); // Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified. key_chord = FixupKeyChord(key_chord); @@ -9562,17 +9727,17 @@ bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, I const int score = CalcRoutingScore(focus_scope_id, owner_id, flags); IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> score %d\n", GetKeyChordName(key_chord), flags, owner_id, score); - if (score == 255) + if (score == 0) return false; // Submit routing for NEXT frame (assuming score is sufficient) - // FIXME: Could expose a way to use a "serve last" policy for same score resolution (using <= instead of <). + // FIXME: Could expose a way to use a "serve last" policy for same score resolution (using >= instead of >). ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); - //const bool set_route = (flags & ImGuiInputFlags_ServeLast) ? (score <= routing_data->RoutingNextScore) : (score < routing_data->RoutingNextScore); - if (score < routing_data->RoutingNextScore) + //const bool set_route = (flags & ImGuiInputFlags_ServeLast) ? (score >= routing_data->RoutingNextScore) : (score > routing_data->RoutingNextScore); + if (score > routing_data->RoutingNextScore) { routing_data->RoutingNext = owner_id; - routing_data->RoutingNextScore = (ImU8)score; + routing_data->RoutingNextScore = (ImU16)score; } // Return routing state for CURRENT frame @@ -9671,14 +9836,14 @@ bool ImGui::IsKeyReleased(ImGuiKey key, ImGuiID owner_id) bool ImGui::IsMouseDown(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); return g.IO.MouseDown[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // should be same as IsKeyDown(MouseButtonToKey(button), ImGuiKeyOwner_Any), but this allows legacy code hijacking the io.Mousedown[] array. } bool ImGui::IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); return g.IO.MouseDown[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyDown(MouseButtonToKey(button), owner_id), but this allows legacy code hijacking the io.Mousedown[] array. } @@ -9690,7 +9855,7 @@ bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat) bool ImGui::IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); if (!g.IO.MouseDown[button]) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership) return false; const float t = g.IO.MouseDownDuration[button]; @@ -9712,14 +9877,14 @@ bool ImGui::IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGui bool ImGui::IsMouseReleased(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // Should be same as IsKeyReleased(MouseButtonToKey(button), ImGuiKeyOwner_Any) } bool ImGui::IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyReleased(MouseButtonToKey(button), owner_id) } @@ -9729,7 +9894,7 @@ bool ImGui::IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id) bool ImGui::IsMouseReleasedWithDelay(ImGuiMouseButton button, float delay) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); 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); } @@ -9737,21 +9902,21 @@ bool ImGui::IsMouseReleasedWithDelay(ImGuiMouseButton button, float delay) bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); } bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button, ImGuiID owner_id) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), owner_id); } int ImGui::GetMouseClickedCount(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); return g.IO.MouseClickedCount[button]; } @@ -9778,7 +9943,7 @@ bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool c bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); if (lock_threshold < 0.0f) lock_threshold = g.IO.MouseDragThreshold; return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold; @@ -9787,7 +9952,7 @@ bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_thresho bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); if (!g.IO.MouseDown[button]) return false; return IsMouseDragPastThreshold(button, lock_threshold); @@ -9834,7 +9999,7 @@ bool ImGui::IsMousePosValid(const ImVec2* mouse_pos) bool ImGui::IsAnyMouseDown() { ImGuiContext& g = *GImGui; - for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++) + for (int n = 0; n < IM_COUNTOF(g.IO.MouseDown); n++) if (g.IO.MouseDown[n]) return true; return false; @@ -9846,7 +10011,7 @@ bool ImGui::IsAnyMouseDown() ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); if (lock_threshold < 0.0f) lock_threshold = g.IO.MouseDragThreshold; if (g.IO.MouseDown[button] || g.IO.MouseReleased[button]) @@ -9859,7 +10024,7 @@ ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold) void ImGui::ResetMouseDragDelta(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown)); // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr g.IO.MouseClickedPos[button] = g.IO.MousePos; } @@ -9975,7 +10140,7 @@ static void ImGui::UpdateMouseInputs() ImGuiIO& io = g.IO; // Mouse Wheel swapping flag - // As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead + // As a standard behavior holding Shift while using Vertical Mouse Wheel triggers Horizontal scroll instead // - We avoid doing it on OSX as it the OS input layer handles this already. // - FIXME: However this means when running on OSX over Emscripten, Shift+WheelY will incur two swapping (1 in OS, 1 here), canceling the feature. // - FIXME: When we can distinguish e.g. touchpad scroll events from mouse ones, we'll set this accordingly based on input source. @@ -10002,7 +10167,7 @@ static void ImGui::UpdateMouseInputs() if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f) g.NavHighlightItemUnderNav = false; - for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) + for (int i = 0; i < IM_COUNTOF(io.MouseDown); i++) { io.MouseClicked[i] = io.MouseDown[i] && io.MouseDownDuration[i] < 0.0f; io.MouseClickedCount[i] = 0; // Will be filled below @@ -10140,8 +10305,9 @@ void ImGui::UpdateMouseWheel() { const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size; SetWindowPos(window, window->Pos + offset, 0); - window->Size = ImTrunc(window->Size * scale); + window->Size = ImTrunc(window->Size * scale); // FIXME: Legacy-ish code, call SetWindowSize()? window->SizeFull = ImTrunc(window->SizeFull * scale); + MarkIniSettingsDirty(window); } return; } @@ -10208,23 +10374,28 @@ void ImGui::SetNextFrameWantCaptureMouse(bool want_capture_mouse) static const char* GetInputSourceName(ImGuiInputSource source) { const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad" }; - IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT && source >= 0 && source < ImGuiInputSource_COUNT); + IM_ASSERT(IM_COUNTOF(input_source_names) == ImGuiInputSource_COUNT); + if (source < 0 || source >= ImGuiInputSource_COUNT) + return "Unknown"; return input_source_names[source]; } static const char* GetMouseSourceName(ImGuiMouseSource source) { const char* mouse_source_names[] = { "Mouse", "TouchScreen", "Pen" }; - IM_ASSERT(IM_ARRAYSIZE(mouse_source_names) == ImGuiMouseSource_COUNT && source >= 0 && source < ImGuiMouseSource_COUNT); + IM_ASSERT(IM_COUNTOF(mouse_source_names) == ImGuiMouseSource_COUNT); + if (source < 0 || source >= ImGuiMouseSource_COUNT) + return "Unknown"; return mouse_source_names[source]; } static void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e) { ImGuiContext& g = *GImGui; + char buf[5]; if (e->Type == ImGuiInputEventType_MousePos) { if (e->MousePos.PosX == -FLT_MAX && e->MousePos.PosY == -FLT_MAX) IMGUI_DEBUG_LOG_IO("[io] %s: MousePos (-FLT_MAX, -FLT_MAX)\n", prefix); else IMGUI_DEBUG_LOG_IO("[io] %s: MousePos (%.1f, %.1f) (%s)\n", prefix, e->MousePos.PosX, e->MousePos.PosY, GetMouseSourceName(e->MousePos.MouseSource)); return; } if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG_IO("[io] %s: MouseButton %d %s (%s)\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up", GetMouseSourceName(e->MouseButton.MouseSource)); return; } if (e->Type == ImGuiInputEventType_MouseWheel) { IMGUI_DEBUG_LOG_IO("[io] %s: MouseWheel (%.3f, %.3f) (%s)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY, GetMouseSourceName(e->MouseWheel.MouseSource)); return; } if (e->Type == ImGuiInputEventType_Key) { IMGUI_DEBUG_LOG_IO("[io] %s: Key \"%s\" %s\n", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? "Down" : "Up"); return; } - if (e->Type == ImGuiInputEventType_Text) { IMGUI_DEBUG_LOG_IO("[io] %s: Text: %c (U+%08X)\n", prefix, e->Text.Char, e->Text.Char); return; } + if (e->Type == ImGuiInputEventType_Text) { ImTextCharToUtf8(buf, e->Text.Char); IMGUI_DEBUG_LOG_IO("[io] %s: Text: '%s' (U+%08X)\n", prefix, buf, e->Text.Char); return; } if (e->Type == ImGuiInputEventType_Focus) { IMGUI_DEBUG_LOG_IO("[io] %s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; } } #endif @@ -10401,7 +10572,7 @@ bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id) ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key); if (owner_id == ImGuiKeyOwner_Any) - return (owner_data->LockThisFrame == false); + return owner_data->LockThisFrame == false; // Note: SetKeyOwner() sets OwnerCurr. It is not strictly required for most mouse routing overlap (because of ActiveId/HoveredId // are acting as filter before this has a chance to filter), but sane as soon as user tries to look into things. @@ -10920,9 +11091,9 @@ void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip() { #ifndef IMGUI_DISABLE_DEBUG_TOOLS ImGuiContext& g = *GImGui; - if (g.DebugDrawIdConflicts != 0 && g.IO.KeyCtrl == false) + if (g.DebugDrawIdConflictsId != 0 && g.IO.KeyCtrl == false) g.DebugDrawIdConflictsCount = g.HoveredIdPreviousFrameItemCount; - if (g.DebugDrawIdConflicts != 0 && g.DebugItemPickerActive == false && BeginErrorTooltip()) + if (g.DebugDrawIdConflictsId != 0 && g.DebugItemPickerActive == false && BeginErrorTooltip()) { Text("Programmer error: %d visible items with conflicting ID!", g.DebugDrawIdConflictsCount); BulletText("Code should use PushID()/PopID() in loops, or append \"##xx\" to same-label identifiers!"); @@ -10932,7 +11103,7 @@ void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip() Separator(); if (g.IO.ConfigDebugHighlightIdConflictsShowItemPicker) { - Text("(Hold CTRL to: use "); + Text("(Hold Ctrl to: use "); SameLine(0.0f, 0.0f); if (SmallButton("Item Picker")) DebugStartItemPicker(); @@ -10941,11 +11112,10 @@ void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip() } else { - Text("(Hold CTRL to "); + Text("(Hold Ctrl to: "); } SameLine(0.0f, 0.0f); - if (SmallButton("Open FAQ->About ID Stack System") && g.PlatformIO.Platform_OpenInShellFn != NULL) - g.PlatformIO.Platform_OpenInShellFn(&g, "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#qa-usage"); + TextLinkOpenURL("read FAQ \"About ID Stack System\"", "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#qa-usage"); SameLine(0.0f, 0.0f); Text(")"); EndErrorTooltip(); @@ -10954,8 +11124,8 @@ void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip() if (g.ErrorCountCurrentFrame > 0 && BeginErrorTooltip()) // Amend at end of frame { Separator(); - Text("(Hold CTRL to:"); - SameLine(); + Text("(Hold Ctrl to: "); + SameLine(0.0f, 0.0f); if (SmallButton("Enable Asserts")) g.IO.ConfigErrorRecoveryEnableAssert = true; //SameLine(); @@ -10968,7 +11138,7 @@ void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip() #endif } -// Pseudo-tooltip. Follow mouse until CTRL is held. When CTRL is held we lock position, allowing to click it. +// Pseudo-tooltip. Follow mouse until Ctrl is held. When Ctrl is held we lock position, allowing to click it. bool ImGui::BeginErrorTooltip() { ImGuiContext& g = *GImGui; @@ -11050,7 +11220,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere. if (!(g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav)) { - // FIMXE-NAV: investigate changing the window tests into a simple 'if (g.NavFocusScopeId == g.CurrentFocusScopeId)' test. + // FIXME-NAV: investigate changing the window tests into a simple 'if (g.NavFocusScopeId == g.CurrentFocusScopeId)' test. window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent); if (g.NavId == id || g.NavAnyRequest) if (g.NavWindow->RootWindowForNav == window->RootWindowForNav) @@ -11091,6 +11261,21 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu // Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use "##something". // READ THE FAQ: https://dearimgui.com/faq IM_ASSERT(id != window->ID && "Cannot have an empty ID at the root of a window. If you need an empty label, use ## and read the FAQ about how the ID Stack works!"); + + // [DEBUG] Highlight all conflicts WITHOUT needing to hover. THIS WILL SLOW DOWN DEAR IMGUI. DON'T KEEP ACTIVATED. + // This will only work for items submitted with ItemAdd(). Some very rare/odd/unrecommended code patterns are calling ButtonBehavior() without ItemAdd(). +#ifdef IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS + if ((g.LastItemData.ItemFlags & ImGuiItemFlags_AllowDuplicateId) == 0) + { + int* p_alive = g.DebugDrawIdConflictsAliveCount.GetIntRef(id, -1); // Could halve lookups if we knew ImGuiStorage can store 64-bit, or by storing FrameCount as 30-bits + highlight as 2-bits. But the point is that we should not pretend that this is fast. + int* p_highlight = g.DebugDrawIdConflictsHighlightSet.GetIntRef(id, -1); + if (*p_alive == g.FrameCount) + *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); + } +#endif } //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG] //if ((g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav) == 0) @@ -11459,6 +11644,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.BackupDeactivatedIdIsAlive = g.DeactivatedItemData.IsAlive; group_data.EmitItem = true; @@ -11523,7 +11709,7 @@ void ImGui::EndGroup() g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; // Forward Edited flag - if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame) + if (g.ActiveIdHasBeenEditedThisFrame && !group_data.BackupActiveIdHasBeenEditedThisFrame) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited; // Forward Deactivated flag @@ -11849,20 +12035,21 @@ bool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags ext } SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f); - //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :( + //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkerboard has issue with transparent colors :( tooltip_flags |= ImGuiTooltipFlags_OverridePrevious; } - const char* window_name_template = is_dragdrop_tooltip ? "##Tooltip_DragDrop_%02d" : "##Tooltip_%02d"; - char window_name[32]; - ImFormatString(window_name, IM_ARRAYSIZE(window_name), window_name_template, g.TooltipOverrideCount); - if ((tooltip_flags & ImGuiTooltipFlags_OverridePrevious) && g.TooltipPreviousWindow != NULL && g.TooltipPreviousWindow->Active) + // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one. + if ((tooltip_flags & ImGuiTooltipFlags_OverridePrevious) && g.TooltipPreviousWindow != NULL && g.TooltipPreviousWindow->Active && !IsWindowInBeginStack(g.TooltipPreviousWindow)) { - // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one. //IMGUI_DEBUG_LOG("[tooltip] '%s' already active, using +1 for this frame\n", window_name); SetWindowHiddenAndSkipItemsForCurrentFrame(g.TooltipPreviousWindow); - ImFormatString(window_name, IM_ARRAYSIZE(window_name), window_name_template, ++g.TooltipOverrideCount); + g.TooltipOverrideCount++; } + + const char* window_name_template = is_dragdrop_tooltip ? "##Tooltip_DragDrop_%02d" : "##Tooltip_%02d"; + char window_name[32]; + ImFormatString(window_name, IM_COUNTOF(window_name), window_name_template, g.TooltipOverrideCount); ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize; Begin(window_name, NULL, flags | extra_window_flags); // 2023-03-09: Added bool return value to the API, but currently always returning true. @@ -12218,7 +12405,7 @@ bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_window_flags) char name[20]; IM_ASSERT((extra_window_flags & ImGuiWindowFlags_ChildMenu) == 0); // Use BeginPopupMenuEx() - ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // No recycling, so we can close/open during the same frame + ImFormatString(name, IM_COUNTOF(name), "##Popup_%08x", id); // No recycling, so we can close/open during the same frame bool is_open = Begin(name, NULL, extra_window_flags | ImGuiWindowFlags_Popup); if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display) @@ -12238,7 +12425,7 @@ bool ImGui::BeginPopupMenuEx(ImGuiID id, const char* label, ImGuiWindowFlags ext char name[128]; IM_ASSERT(extra_window_flags & ImGuiWindowFlags_ChildMenu); - ImFormatString(name, IM_ARRAYSIZE(name), "%s###Menu_%02d", label, g.BeginMenuDepth); // Recycle windows based on depth + ImFormatString(name, IM_COUNTOF(name), "%s###Menu_%02d", label, g.BeginMenuDepth); // Recycle windows based on depth bool is_open = Begin(name, NULL, extra_window_flags | ImGuiWindowFlags_Popup); if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display) EndPopup(); @@ -12406,10 +12593,10 @@ ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& s // Combo Box policy (we want a connecting edge) if (policy == ImGuiPopupPositionPolicy_ComboBox) { - const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up }; + const ImGuiDir dir_preferred_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up }; for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++) { - const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n]; + const ImGuiDir dir = (n == -1) ? *last_dir : dir_preferred_order[n]; if (n != -1 && dir == *last_dir) // Already tried this direction? continue; ImVec2 pos; @@ -12428,10 +12615,10 @@ ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& s // (Always first try the direction we used on the last frame, if any) if (policy == ImGuiPopupPositionPolicy_Tooltip || policy == ImGuiPopupPositionPolicy_Default) { - const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left }; + const ImGuiDir dir_preferred_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left }; for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++) { - const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n]; + const ImGuiDir dir = (n == -1) ? *last_dir : dir_preferred_order[n]; if (n != -1 && dir == *last_dir) // Already tried this direction? continue; @@ -12592,13 +12779,13 @@ bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) IM_ASSERT(cur_window); // Not inside a Begin()/End() const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0; - if (flags & ImGuiHoveredFlags_RootWindow) + if (flags & ImGuiFocusedFlags_RootWindow) cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy); - if (flags & ImGuiHoveredFlags_ChildWindows) + if (flags & ImGuiFocusedFlags_ChildWindows) return IsWindowChildOf(ref_window, cur_window, popup_hierarchy); else - return (ref_window == cur_window); + return ref_window == cur_window; } static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) @@ -12917,7 +13104,7 @@ static float inline NavScoreItemDistInterval(float cand_min, float cand_max, flo } // Scoring function for keyboard/gamepad directional navigation. Based on https://gist.github.com/rygorous/6981057 -static bool ImGui::NavScoreItem(ImGuiNavItemData* result) +static bool ImGui::NavScoreItem(ImGuiNavItemData* result, const ImRect& nav_bb) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -12925,7 +13112,7 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result) return false; // FIXME: Those are not good variables names - ImRect cand = g.LastItemData.NavRect; // Current item nav rectangle + ImRect cand = nav_bb; // Current item nav rectangle const ImRect curr = g.NavScoringRect; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width) g.NavScoringDebugCount++; @@ -12982,11 +13169,11 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result) const ImGuiDir move_dir = g.NavMoveDir; #if IMGUI_DEBUG_NAV_SCORING char buf[200]; - if (g.IO.KeyCtrl) // Hold CTRL to preview score in matching quadrant. CTRL+Arrow to rotate. + if (g.IO.KeyCtrl) // Hold Ctrl to preview score in matching quadrant. Ctrl+Arrow to rotate. { if (quadrant == move_dir) { - ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center); + ImFormatString(buf, IM_COUNTOF(buf), "%.0f/%.0f", dist_box, dist_center); ImDrawList* draw_list = GetForegroundDrawList(window); draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 80)); draw_list->AddRectFilled(cand.Min, cand.Min + CalcTextSize(buf), IM_COL32(255, 0, 0, 200)); @@ -12997,7 +13184,7 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result) const bool debug_tty = (g.IO.KeyCtrl && IsKeyPressed(ImGuiKey_Space)); if (debug_hovering || debug_tty) { - ImFormatString(buf, IM_ARRAYSIZE(buf), + ImFormatString(buf, IM_COUNTOF(buf), "d-box (%7.3f,%7.3f) -> %7.3f\nd-center (%7.3f,%7.3f) -> %7.3f\nd-axial (%7.3f,%7.3f) -> %7.3f\nnav %c, quadrant %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "-WENS"[move_dir+1], "-WENS"[quadrant+1]); if (debug_hovering) @@ -13092,13 +13279,13 @@ static void ImGui::NavProcessItem() const ImGuiID id = g.LastItemData.ID; const ImGuiItemFlags item_flags = g.LastItemData.ItemFlags; - // When inside a container that isn't scrollable with Left<>Right, clip NavRect accordingly (#2221) + // When inside a container that isn't scrollable with Left<>Right, clip NavRect accordingly (#2221, #8816) + ImRect nav_bb = g.LastItemData.NavRect; if (window->DC.NavIsScrollPushableX == false) { - g.LastItemData.NavRect.Min.x = ImClamp(g.LastItemData.NavRect.Min.x, window->ClipRect.Min.x, window->ClipRect.Max.x); - g.LastItemData.NavRect.Max.x = ImClamp(g.LastItemData.NavRect.Max.x, window->ClipRect.Min.x, window->ClipRect.Max.x); + nav_bb.Min.x = ImClamp(nav_bb.Min.x, window->ClipRect.Min.x, window->ClipRect.Max.x); + nav_bb.Max.x = ImClamp(nav_bb.Max.x, window->ClipRect.Min.x, window->ClipRect.Max.x); } - const ImRect nav_bb = g.LastItemData.NavRect; // Process Init Request if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent && (item_flags & ImGuiItemFlags_Disabled) == 0) @@ -13130,15 +13317,19 @@ static void ImGui::NavProcessItem() else if (g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) { ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; - if (NavScoreItem(result)) + if (NavScoreItem(result, nav_bb)) NavApplyItemToResult(result); // Features like PageUp/PageDown need to maintain a separate score for the visible set of items. const float VISIBLE_RATIO = 0.70f; - if ((g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb)) - if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO) - if (NavScoreItem(&g.NavMoveResultLocalVisible)) - NavApplyItemToResult(&g.NavMoveResultLocalVisible); + if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) + { + const ImRect& r = window->InnerRect; // window->ClipRect + if (r.Overlaps(nav_bb)) + if (ImClamp(nav_bb.Max.y, r.Min.y, r.Max.y) - ImClamp(nav_bb.Min.y, r.Min.y, r.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO) + if (NavScoreItem(&g.NavMoveResultLocalVisible, nav_bb)) + NavApplyItemToResult(&g.NavMoveResultLocalVisible); + } } } } @@ -13417,6 +13608,9 @@ static ImVec2 ImGui::NavCalcPreferredRefPos() const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID; + if (source != ImGuiInputSource_Mouse && !activated_shortcut && window == NULL) + source = ImGuiInputSource_Mouse; + // Testing for !activated_shortcut here could in theory be removed if we decided that activating a remote shortcut altered one of the g.NavDisableXXX flag. if (source == ImGuiInputSource_Mouse) { @@ -13432,11 +13626,11 @@ static ImVec2 ImGui::NavCalcPreferredRefPos() ImRect ref_rect; if (activated_shortcut) ref_rect = g.LastItemData.NavRect; - else + else if (window != NULL) ref_rect = WindowRectRelToAbs(window, window->NavRectRel[g.NavLayer]); // Take account of upcoming scrolling (maybe set mouse pos should be done in EndFrame?) - if (window->LastFrameActive != g.FrameCount && (window->ScrollTarget.x != FLT_MAX || window->ScrollTarget.y != FLT_MAX)) + if (window != NULL && window->LastFrameActive != g.FrameCount && (window->ScrollTarget.x != FLT_MAX || window->ScrollTarget.y != FLT_MAX)) { ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); ref_rect.Translate(window->Scroll - next_scroll); @@ -13525,7 +13719,7 @@ static void ImGui::NavUpdate() if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main) g.NavWindow->NavLastChildNavWindow = NULL; - // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.) + // Update Ctrl+Tab and Windowing features (hold Square to move/resize/etc.) NavUpdateWindowing(); // Set output flags for user application @@ -13733,16 +13927,11 @@ void ImGui::NavUpdateCreateMoveRequest() // Update PageUp/PageDown/Home/End scroll // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag? - float scoring_rect_offset_y = 0.0f; + float scoring_page_offset_y = 0.0f; if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active) - scoring_rect_offset_y = NavUpdatePageUpPageDown(); - if (scoring_rect_offset_y != 0.0f) - { - g.NavScoringNoClipRect = window->InnerRect; - g.NavScoringNoClipRect.TranslateY(scoring_rect_offset_y); - } + scoring_page_offset_y = NavUpdatePageUpPageDown(); - // [DEBUG] Always send a request when holding CTRL. Hold CTRL + Arrow change the direction. + // [DEBUG] Always send a request when holding Ctrl. Hold Ctrl + Arrow change the direction. #if IMGUI_DEBUG_NAV_SCORING //if (io.KeyCtrl && IsKeyPressed(ImGuiKey_C)) // g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3); @@ -13797,18 +13986,30 @@ void ImGui::NavUpdateCreateMoveRequest() } } + // Prepare scoring rectangle. // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items) ImRect scoring_rect; if (window != NULL) { ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0); scoring_rect = WindowRectRelToAbs(window, nav_rect_rel); - scoring_rect.TranslateY(scoring_rect_offset_y); + + if (g.NavMoveFlags & ImGuiNavMoveFlags_IsPageMove) + { + // When we start from a visible location, score visible items and prioritize this result. + if (window->InnerRect.Contains(scoring_rect)) + g.NavMoveFlags |= ImGuiNavMoveFlags_AlsoScoreVisibleSet; + g.NavScoringNoClipRect = scoring_rect; + scoring_rect.TranslateY(scoring_page_offset_y); + g.NavScoringNoClipRect.Add(scoring_rect); + } + + //GetForegroundDrawList()->AddRectFilled(scoring_rect.Min - ImVec2(1, 1), scoring_rect.Max + ImVec2(1, 1), IM_COL32(255, 100, 0, 80)); // [DEBUG] Pre-bias if (g.NavMoveSubmitted) NavBiasScoringRect(scoring_rect, window->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer], g.NavMoveDir, g.NavMoveFlags); IM_ASSERT(!scoring_rect.IsInverted()); // Ensure we have a non-inverted bounding box here will allow us to remove extraneous ImFabs() calls in NavScoreItem(). - //GetForegroundDrawList()->AddRect(scoring_rect.Min, scoring_rect.Max, IM_COL32(255,200,0,255)); // [DEBUG] - //if (!g.NavScoringNoClipRect.IsInverted()) { GetForegroundDrawList()->AddRect(g.NavScoringNoClipRect.Min, g.NavScoringNoClipRect.Max, IM_COL32(255, 200, 0, 255)); } // [DEBUG] + //GetForegroundDrawList()->AddRectFilled(scoring_rect.Min - ImVec2(1, 1), scoring_rect.Max + ImVec2(1, 1), IM_COL32(255, 100, 0, 80)); // [DEBUG] Post-bias + //if (!g.NavScoringNoClipRect.IsInverted()) { GetForegroundDrawList()->AddRectFilled(g.NavScoringNoClipRect.Min, g.NavScoringNoClipRect.Max, IM_COL32(100, 255, 0, 80)); } // [DEBUG] } g.NavScoringRect = scoring_rect; //g.NavScoringNoClipRect.Add(scoring_rect); @@ -13946,6 +14147,8 @@ void ImGui::NavMoveRequestApplyResult() { g.NavNextActivateId = result->ID; g.NavNextActivateFlags = ImGuiActivateFlags_None; + if (g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi) + g.NavNextActivateFlags |= ImGuiActivateFlags_FromFocusApi; if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) g.NavNextActivateFlags |= ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState | ImGuiActivateFlags_FromTabbing; } @@ -14055,14 +14258,14 @@ static float ImGui::NavUpdatePageUpPageDown() nav_scoring_rect_offset_y = -page_offset_y; g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item) g.NavMoveClipDir = ImGuiDir_Up; - g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_IsPageMove; // ImGuiNavMoveFlags_AlsoScoreVisibleSet may be added later } else if (IsKeyPressed(ImGuiKey_PageDown, true)) { nav_scoring_rect_offset_y = +page_offset_y; g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item) g.NavMoveClipDir = ImGuiDir_Down; - g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_IsPageMove; // ImGuiNavMoveFlags_AlsoScoreVisibleSet may be added later } else if (home_pressed) { @@ -14094,7 +14297,7 @@ static void ImGui::NavEndFrame() { ImGuiContext& g = *GImGui; - // Show CTRL+TAB list window + // Show Ctrl+Tab list window if (g.NavWindowingTarget != NULL) NavUpdateWindowingOverlay(); @@ -14166,8 +14369,8 @@ static void ImGui::NavUpdateCreateWrappingRequest() NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags); } -// Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext) -// Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmatically. +// Can we focus this window with Ctrl+Tab (or PadMenu + PadFocusPrev/PadFocusNext) +// Note that NoNavFocus makes the window not reachable with Ctrl+Tab but it can still be focused with mouse or programmatically. // If you want a window to never be focused, you may use the e.g. NoInputs flag. bool ImGui::IsWindowNavFocusable(ImGuiWindow* window) { @@ -14212,13 +14415,14 @@ static void ImGui::NavUpdateWindowingApplyFocus(ImGuiWindow* apply_focus_window) SetNavCursorVisibleAfterMove(); ClosePopupsOverWindow(apply_focus_window, false); FocusWindow(apply_focus_window, ImGuiFocusRequestFlags_RestoreFocusedChild); + IM_ASSERT(g.NavWindow != NULL); apply_focus_window = g.NavWindow; if (apply_focus_window->NavLastIds[0] == 0) NavInitWindow(apply_focus_window, false); // If the window has ONLY a menu layer (no main layer), select it directly // Use NavLayersActiveMaskNext since windows didn't have a chance to be Begin()-ed on this frame, - // so CTRL+Tab where the keys are only held for 1 frame will be able to use correct layers mask since + // so Ctrl+Tab where the keys are only held for 1 frame will be able to use correct layers mask since // the target window as already been previewed once. // FIXME-NAV: This should be done in NavInit.. or in FocusWindow... However in both of those cases, // we won't have a guarantee that windows has been visible before and therefore NavLayersActiveMask* @@ -14230,7 +14434,7 @@ static void ImGui::NavUpdateWindowingApplyFocus(ImGuiWindow* apply_focus_window) } // Windowing management mode -// Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer) +// Keyboard: Ctrl+Tab (change focus/move/resize), Alt (toggle menu layer) // Gamepad: Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer) static void ImGui::NavUpdateWindowing() { @@ -14241,7 +14445,7 @@ static void ImGui::NavUpdateWindowing() bool apply_toggle_layer = false; ImGuiWindow* modal_window = GetTopMostPopupModal(); - bool allow_windowing = false;// (modal_window == NULL); // FIXME: This prevent CTRL+TAB from being usable with windows that are inside the Begin-stack of that modal. + bool allow_windowing = false;// (modal_window == NULL); // FIXME: This prevent Ctrl+Tab from being usable with windows that are inside the Begin-stack of that modal. if (!allow_windowing) g.NavWindowingTarget = NULL; @@ -14253,24 +14457,30 @@ static void ImGui::NavUpdateWindowing() g.NavWindowingTargetAnim = NULL; } - // Start CTRL+Tab or Square+L/R window selection + // Start Ctrl+Tab or Square+L/R window selection // (g.ConfigNavWindowingKeyNext/g.ConfigNavWindowingKeyPrev defaults are ImGuiMod_Ctrl|ImGuiKey_Tab and ImGuiMod_Ctrl|ImGuiMod_Shift|ImGuiKey_Tab) const ImGuiID owner_id = ImHashStr("##NavUpdateWindowing"); const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; const bool keyboard_next_window = allow_windowing && g.ConfigNavWindowingKeyNext && Shortcut(g.ConfigNavWindowingKeyNext, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id); const bool keyboard_prev_window = allow_windowing && g.ConfigNavWindowingKeyPrev && Shortcut(g.ConfigNavWindowingKeyPrev, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id); - const bool start_windowing_with_gamepad = allow_windowing && nav_gamepad_active && !g.NavWindowingTarget && Shortcut(ImGuiKey_NavGamepadMenu, ImGuiInputFlags_RouteAlways, owner_id); + const bool start_toggling_with_gamepad = nav_gamepad_active && !g.NavWindowingTarget && Shortcut(ImGuiKey_NavGamepadMenu, ImGuiInputFlags_RouteAlways, owner_id); + const bool start_windowing_with_gamepad = allow_windowing && start_toggling_with_gamepad; const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && (keyboard_next_window || keyboard_prev_window); // Note: enabled even without NavEnableKeyboard! bool just_started_windowing_from_null_focus = false; + if (start_toggling_with_gamepad) + { + g.NavWindowingToggleLayer = true; // Gamepad starts toggling layer + g.NavWindowingToggleKey = ImGuiKey_NavGamepadMenu; + g.NavWindowingInputSource = g.NavInputSource = ImGuiInputSource_Gamepad; + } if (start_windowing_with_gamepad || start_windowing_with_keyboard) - if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) + if (ImGuiWindow* window = (g.NavWindow && IsWindowNavFocusable(g.NavWindow)) ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) { if (start_windowing_with_keyboard || g.ConfigNavWindowingWithGamepad) g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // Current location g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f; g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f); - g.NavWindowingToggleLayer = start_windowing_with_gamepad ? true : false; // Gamepad starts toggling layer g.NavWindowingInputSource = g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad; if (g.NavWindow == NULL) just_started_windowing_from_null_focus = true; @@ -14315,7 +14525,7 @@ static void ImGui::NavUpdateWindowing() // Keyboard: Focus if (g.NavWindowingTarget && g.NavWindowingInputSource == ImGuiInputSource_Keyboard) { - // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise + // Visuals only appears after a brief time after pressing TAB the first time, so that a fast Ctrl+Tab doesn't add visual noise ImGuiKeyChord shared_mods = ((g.ConfigNavWindowingKeyNext ? g.ConfigNavWindowingKeyNext : ImGuiMod_Mask_) & (g.ConfigNavWindowingKeyPrev ? g.ConfigNavWindowingKeyPrev : ImGuiMod_Mask_)) & ImGuiMod_Mask_; IM_ASSERT(shared_mods != 0); // Next/Prev shortcut currently needs a shared modifier to "hold", otherwise Prev actions would keep cycling between two windows. g.NavWindowingTimer += io.DeltaTime; @@ -14326,7 +14536,7 @@ static void ImGui::NavUpdateWindowing() apply_focus_window = g.NavWindowingTarget; } - // Keyboard: Press and Release ALT to toggle menu layer + // Keyboard: Press and Release Alt to toggle menu layer const ImGuiKey windowing_toggle_keys[] = { ImGuiKey_LeftAlt, ImGuiKey_RightAlt }; bool windowing_toggle_layer_start = false; if (g.NavWindow != NULL && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) @@ -14432,7 +14642,7 @@ static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window) return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingUntitled); } -// Overlay displayed when using CTRL+TAB. Called by EndFrame(). +// Overlay displayed when using Ctrl+Tab. Called by EndFrame(). void ImGui::NavUpdateWindowingOverlay() { ImGuiContext& g = *GImGui; @@ -14441,13 +14651,12 @@ void ImGui::NavUpdateWindowingOverlay() if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY) return; - if (g.NavWindowingListWindow == NULL) - g.NavWindowingListWindow = FindWindowByName("##NavWindowingOverlay"); const ImGuiViewport* viewport = GetMainViewport(); SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX)); SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f)); PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f); Begin("##NavWindowingOverlay", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings); + g.NavWindowingListWindow = g.CurrentWindow; if (g.ContextName[0] != 0) SeparatorText(g.ContextName); for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--) @@ -14482,7 +14691,7 @@ void ImGui::ClearDragDrop() IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] ClearDragDrop()\n"); g.DragDropActive = false; g.DragDropPayload.Clear(); - g.DragDropAcceptFlags = ImGuiDragDropFlags_None; + g.DragDropAcceptFlagsCurr = ImGuiDragDropFlags_None; g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0; g.DragDropAcceptIdCurrRectSurface = FLT_MAX; g.DragDropAcceptFrameCount = -1; @@ -14611,11 +14820,11 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit) // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents. bool ret; - if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) + if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlagsPrev & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) ret = BeginTooltipHidden(); else ret = BeginTooltip(); - IM_ASSERT(ret); // FIXME-NEWBEGIN: If this ever becomes false, we need to Begin("##Hidden", NULL, ImGuiWindowFlags_NoSavedSettings) + SetWindowHiddendAndSkipItemsForCurrentFrame(). + IM_ASSERT(ret); // FIXME-NEWBEGIN: If this ever becomes false, we need to Begin("##Hidden", NULL, ImGuiWindowFlags_NoSavedSettings) + SetWindowHiddenAndSkipItemsForCurrentFrame(). IM_UNUSED(ret); } @@ -14649,7 +14858,7 @@ bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_s cond = ImGuiCond_Always; IM_ASSERT(type != NULL); - IM_ASSERT(ImStrlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long"); + IM_ASSERT(ImStrlen(type) < IM_COUNTOF(payload.DataType) && "Payload type can be at most 32 characters long"); IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0)); IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once); IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource() @@ -14657,7 +14866,7 @@ bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_s if (cond == ImGuiCond_Always || payload.DataFrameCount == -1) { // Copy payload - ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType)); + ImStrncpy(payload.DataType, type, IM_COUNTOF(payload.DataType)); g.DragDropPayloadBufHeap.resize(0); if (data_size > sizeof(g.DragDropPayloadBufLocal)) { @@ -14705,6 +14914,31 @@ bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id) g.DragDropTargetRect = bb; g.DragDropTargetClipRect = window->ClipRect; // May want to be overridden by user depending on use case? g.DragDropTargetId = id; + g.DragDropTargetFullViewport = 0; + g.DragDropWithinTarget = true; + return true; +} + +// Typical usage would be: +// if (!ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) +// if (ImGui::BeginDragDropTargetViewport(ImGui::GetMainViewport(), NULL)) +// But we are leaving the hover test to the caller for maximum flexibility. +bool ImGui::BeginDragDropTargetViewport(ImGuiViewport* viewport, const ImRect* p_bb) +{ + ImGuiContext& g = *GImGui; + if (!g.DragDropActive) + return false; + + ImRect bb = p_bb ? *p_bb : ((ImGuiViewportP*)viewport)->GetWorkRect(); + ImGuiID id = viewport->ID; + if (!IsMouseHoveringRect(bb.Min, bb.Max, false) || (id == g.DragDropPayload.SourceId)) + return false; + + IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget() + g.DragDropTargetRect = bb; + g.DragDropTargetClipRect = bb; + g.DragDropTargetId = id; + g.DragDropTargetFullViewport = id; g.DragDropWithinTarget = true; return true; } @@ -14767,7 +15001,7 @@ const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDrop if (r_surface > g.DragDropAcceptIdCurrRectSurface) return NULL; - g.DragDropAcceptFlags = flags; + g.DragDropAcceptFlagsCurr = flags; g.DragDropAcceptIdCurr = g.DragDropTargetId; g.DragDropAcceptIdCurrRectSurface = r_surface; //IMGUI_DEBUG_LOG("AcceptDragDropPayload(): %08X: accept\n", g.DragDropTargetId); @@ -14775,8 +15009,17 @@ const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDrop // Render default drop visuals payload.Preview = was_accepted_previously; flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that live for 1 frame) - if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview) - RenderDragDropTargetRect(r, g.DragDropTargetClipRect); + const bool draw_target_rect = payload.Preview && !(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); + if (draw_target_rect && g.DragDropTargetFullViewport != 0) + { + ImRect bb = g.DragDropTargetRect; + bb.Expand(-3.5f); + RenderDragDropTargetRectEx(GetForegroundDrawList(), bb); + } + else if (draw_target_rect) + { + RenderDragDropTargetRectForItem(r); + } g.DragDropAcceptFrameCount = g.FrameCount; if ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) && g.DragDropMouseButton == -1) @@ -14792,21 +15035,28 @@ const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDrop } // FIXME-STYLE FIXME-DRAGDROP: Settle on a proper default visuals for drop target. -void ImGui::RenderDragDropTargetRect(const ImRect& bb, const ImRect& item_clip_rect) +void ImGui::RenderDragDropTargetRectForItem(const ImRect& bb) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImRect bb_display = bb; - bb_display.ClipWith(item_clip_rect); // Clip THEN expand so we have a way to visualize that target is not entirely visible. - bb_display.Expand(3.5f); + bb_display.ClipWith(g.DragDropTargetClipRect); // Clip THEN expand so we have a way to visualize that target is not entirely visible. + bb_display.Expand(g.Style.DragDropTargetPadding); bool push_clip_rect = !window->ClipRect.Contains(bb_display); if (push_clip_rect) window->DrawList->PushClipRectFullScreen(); - window->DrawList->AddRect(bb_display.Min, bb_display.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f); // FIXME-DPI + RenderDragDropTargetRectEx(window->DrawList, bb_display); if (push_clip_rect) window->DrawList->PopClipRect(); } +void ImGui::RenderDragDropTargetRectEx(ImDrawList* draw_list, const ImRect& bb) +{ + ImGuiContext& g = *GImGui; + draw_list->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_DragDropTargetBg), g.Style.DragDropTargetRounding, 0); + draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_DragDropTarget), g.Style.DragDropTargetRounding, 0, g.Style.DragDropTargetBorderSize); +} + const ImGuiPayload* ImGui::GetDragDropPayload() { ImGuiContext& g = *GImGui; @@ -15059,7 +15309,7 @@ void ImGui::LogButtons() const bool log_to_file = Button("Log To File"); SameLine(); const bool log_to_clipboard = Button("Log To Clipboard"); SameLine(); PushItemFlag(ImGuiItemFlags_NoTabStop, true); - SetNextItemWidth(80.0f); + SetNextItemWidth(CalcTextSize("999").x); SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL); PopItemFlag(); PopID(); @@ -15286,13 +15536,9 @@ ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name) { ImGuiContext& g = *GImGui; + // Preserve the full string when ConfigDebugVerboseIniSettings is set to make .ini inspection easier. if (g.IO.ConfigDebugIniSettings == false) - { - // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() - // Preserve the full string when ConfigDebugVerboseIniSettings is set to make .ini inspection easier. - if (const char* p = strstr(name, "###")) - name = p; - } + name = ImHashSkipUncontributingPrefix(name); const size_t name_len = ImStrlen(name); // Allocate chunk @@ -15453,6 +15699,23 @@ void ImGui::LocalizeRegisterEntries(const ImGuiLocEntry* entries, int count) // (this section is more complete in the 'docking' branch) //----------------------------------------------------------------------------- +void ImGuiPlatformIO::ClearPlatformHandlers() +{ + Platform_GetClipboardTextFn = NULL; + Platform_SetClipboardTextFn = NULL; + Platform_ClipboardUserData = NULL; + Platform_OpenInShellFn = NULL; + Platform_OpenInShellUserData = NULL; + Platform_SetImeDataFn = NULL; + Platform_ImeUserData = NULL; +} + +void ImGuiPlatformIO::ClearRendererHandlers() +{ + Renderer_TextureMaxWidth = Renderer_TextureMaxHeight = 0; + Renderer_RenderState = NULL; +} + ImGuiViewport* ImGui::GetMainViewport() { ImGuiContext& g = *GImGui; @@ -15750,10 +16013,14 @@ static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport*, ImG //----------------------------------------------------------------------------- // [SECTION] METRICS/DEBUGGER WINDOW //----------------------------------------------------------------------------- +// - MetricsHelpMarker() [Internal] // - DebugRenderViewportThumbnail() [Internal] // - RenderViewportsThumbnails() [Internal] +// - DebugRenderKeyboardPreview() [Internal] // - DebugTextEncoding() -// - MetricsHelpMarker() [Internal] +// - DebugFlashStyleColorStop() [Internal] +// - DebugFlashStyleColor() +// - UpdateDebugToolFlashStyleColor() [Internal] // - ShowFontAtlas() [Internal but called by Demo!] // - DebugNodeTexture() [Internal] // - ShowMetricsWindow() @@ -15769,8 +16036,24 @@ static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport*, ImG // - DebugNodeWindowSettings() [Internal] // - DebugNodeWindowsList() [Internal] // - DebugNodeWindowsListByBeginStackParent() [Internal] +// - ShowFontSelector() //----------------------------------------------------------------------------- +#if !defined(IMGUI_DISABLE_DEMO_WINDOWS) || !defined(IMGUI_DISABLE_DEBUG_TOOLS) +// Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds. +static void MetricsHelpMarker(const char* desc) +{ + ImGui::TextDisabled("(?)"); + if (ImGui::BeginItemTooltip()) + { + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::TextUnformatted(desc); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } +} +#endif + #ifndef IMGUI_DISABLE_DEBUG_TOOLS void ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb) @@ -15854,7 +16137,7 @@ void ImGui::DebugRenderKeyboardPreview(ImDrawList* draw_list) if (!IsItemVisible()) return; draw_list->PushClipRect(board_min, board_max, true); - for (int n = 0; n < IM_ARRAYSIZE(keys_to_display); n++) + for (int n = 0; n < IM_COUNTOF(keys_to_display); n++) { const KeyLayoutData* key_data = &keys_to_display[n]; ImVec2 key_min = ImVec2(start_pos.x + key_data->Col * key_step.x + key_data->Row * key_row_offset, start_pos.y + key_data->Row * key_step.y); @@ -15884,10 +16167,11 @@ void ImGui::DebugTextEncoding(const char* str) TableSetupColumn("Glyph"); TableSetupColumn("Codepoint"); TableHeadersRow(); + const char* str_end = str + strlen(str); // As we may receive malformed UTF-8, pass an explicit end instead of relying on ImTextCharFromUtf8() assuming enough space. for (const char* p = str; *p != 0; ) { unsigned int c; - const int c_utf8_len = ImTextCharFromUtf8(&c, p, NULL); + const int c_utf8_len = ImTextCharFromUtf8(&c, p, str_end); TableNextColumn(); Text("%d", (int)(p - str)); TableNextColumn(); @@ -15940,36 +16224,21 @@ void ImGui::UpdateDebugToolFlashStyleColor() DebugFlashStyleColorStop(); } -static const char* FormatTextureIDForDebugDisplay(char* buf, int buf_size, ImTextureID tex_id) +ImU64 ImGui::DebugTextureIDToU64(ImTextureID tex_id) { - union { void* ptr; int integer; } tex_id_opaque; - memcpy(&tex_id_opaque, &tex_id, ImMin(sizeof(void*), sizeof(tex_id))); - if (sizeof(tex_id) >= sizeof(void*)) - ImFormatString(buf, buf_size, "0x%p", tex_id_opaque.ptr); - else - ImFormatString(buf, buf_size, "0x%04X", tex_id_opaque.integer); - return buf; + ImU64 v = 0; + memcpy(&v, &tex_id, ImMin(sizeof(ImU64), sizeof(ImTextureID))); + return v; } -static const char* FormatTextureIDForDebugDisplay(char* buf, int buf_size, const ImDrawCmd* cmd) +static const char* FormatTextureRefForDebugDisplay(char* buf, int buf_size, ImTextureRef tex_ref) { + char* buf_p = buf; char* buf_end = buf + buf_size; - if (cmd->TexRef._TexData != NULL) - buf += ImFormatString(buf, buf_end - buf, "#%03d: ", cmd->TexRef._TexData->UniqueID); - return FormatTextureIDForDebugDisplay(buf, (int)(buf_end - buf), cmd->TexRef.GetTexID()); // Calling TexRef::GetTexID() to avoid assert of cmd->GetTexID() -} - -// Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds. -static void MetricsHelpMarker(const char* desc) -{ - ImGui::TextDisabled("(?)"); - if (ImGui::BeginItemTooltip()) - { - ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); - ImGui::TextUnformatted(desc); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); - } + if (tex_ref._TexData != NULL) + buf_p += ImFormatString(buf_p, buf_end - buf_p, "#%03d: ", tex_ref._TexData->UniqueID); + ImFormatString(buf_p, buf_end - buf_p, "0x%X", ImGui::DebugTextureIDToU64(tex_ref.GetTexID())); + return buf; } #ifdef IMGUI_ENABLE_FREETYPE @@ -15992,13 +16261,18 @@ void ImGui::ShowFontAtlas(ImFontAtlas* atlas) style._NextFrameFontSizeBase = style.FontSizeBase; // FIXME: Temporary hack until we finish remaining work. SameLine(0.0f, 0.0f); Text(" (out %.2f)", GetFontSize()); SameLine(); MetricsHelpMarker("- This is scaling font only. General scaling will come later."); - DragFloat("FontScaleMain", &style.FontScaleMain, 0.02f, 0.5f, 5.0f); + DragFloat("FontScaleMain", &style.FontScaleMain, 0.02f, 0.5f, 4.0f); //BeginDisabled(io.ConfigDpiScaleFonts); - DragFloat("FontScaleDpi", &style.FontScaleDpi, 0.02f, 0.5f, 5.0f); + DragFloat("FontScaleDpi", &style.FontScaleDpi, 0.02f, 0.5f, 4.0f); //SetItemTooltip("When io.ConfigDpiScaleFonts is set, this value is automatically overwritten."); //EndDisabled(); if ((io.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0) + { BulletText("Warning: Font scaling will NOT be smooth, because\nImGuiBackendFlags_RendererHasTextures is not set!"); + BulletText("For instructions, see:"); + SameLine(); + TextLinkOpenURL("docs/BACKENDS.md", "https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md"); + } BulletText("Load a nice font for better results!"); BulletText("Please submit feedback:"); SameLine(); TextLinkOpenURL("#8465", "https://github.com/ocornut/imgui/issues/8465"); @@ -16019,7 +16293,7 @@ void ImGui::ShowFontAtlas(ImFontAtlas* atlas) #ifdef IMGUI_ENABLE_STB_TRUETYPE const ImFontLoader* loader_stbtruetype = ImFontAtlasGetFontLoaderForStbTruetype(); if (RadioButton("stb_truetype", loader_current == loader_stbtruetype)) - ImFontAtlasBuildSetupFontLoader(atlas, loader_stbtruetype); + atlas->SetFontLoader(loader_stbtruetype); #else BeginDisabled(); RadioButton("stb_truetype", false); @@ -16030,7 +16304,7 @@ void ImGui::ShowFontAtlas(ImFontAtlas* atlas) #ifdef IMGUI_ENABLE_FREETYPE const ImFontLoader* loader_freetype = ImGuiFreeType::GetFontLoader(); if (RadioButton("FreeType", loader_current == loader_freetype)) - ImFontAtlasBuildSetupFontLoader(atlas, loader_freetype); + atlas->SetFontLoader(loader_freetype); if (loader_current == loader_freetype) { unsigned int loader_flags = atlas->FontLoaderFlags; @@ -16150,9 +16424,9 @@ void ImGui::DebugNodeTexture(ImTextureData* tex, int int_id, const ImFontAtlasRe } PopStyleVar(); - char texid_desc[30]; + char texref_desc[30]; Text("Status = %s (%d), Format = %s (%d), UseColors = %d", ImTextureDataGetStatusName(tex->Status), tex->Status, ImTextureDataGetFormatName(tex->Format), tex->Format, tex->UseColors); - Text("TexID = %s, BackendUserData = %p", FormatTextureIDForDebugDisplay(texid_desc, IM_ARRAYSIZE(texid_desc), tex->TexID), tex->BackendUserData); + Text("TexRef = %s, BackendUserData = %p", FormatTextureRefForDebugDisplay(texref_desc, IM_COUNTOF(texref_desc), tex->GetTexRef()), tex->BackendUserData); TreePop(); } PopID(); @@ -16238,6 +16512,10 @@ void ImGui::ShowMetricsWindow(bool* p_open) } }; +#ifdef IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS + TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS is enabled.\nMust disable after use! Otherwise Dear ImGui will run slower.\n"); +#endif + // Tools if (TreeNode("Tools")) { @@ -16291,7 +16569,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) BulletText("Table 0x%08X (%d columns, in '%s')", table->ID, table->ColumnsCount, table->OuterWindow->Name); if (IsItemHovered()) - GetForegroundDrawList()->AddRect(table->OuterRect.Min - ImVec2(1, 1), table->OuterRect.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f); + GetForegroundDrawList(table->OuterWindow)->AddRect(table->OuterRect.Min - ImVec2(1, 1), table->OuterRect.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f); Indent(); char buf[128]; for (int rect_n = 0; rect_n < TRT_Count; rect_n++) @@ -16303,19 +16581,19 @@ void ImGui::ShowMetricsWindow(bool* p_open) for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { ImRect r = Funcs::GetTableRect(table, rect_n, column_n); - ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) Col %d %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), column_n, trt_rects_names[rect_n]); + ImFormatString(buf, IM_COUNTOF(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) Col %d %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), column_n, trt_rects_names[rect_n]); Selectable(buf); if (IsItemHovered()) - GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f); + GetForegroundDrawList(table->OuterWindow)->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f); } } else { ImRect r = Funcs::GetTableRect(table, rect_n, -1); - ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), trt_rects_names[rect_n]); + ImFormatString(buf, IM_COUNTOF(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), trt_rects_names[rect_n]); Selectable(buf); if (IsItemHovered()) - GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f); + GetForegroundDrawList(table->OuterWindow)->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f); } } Unindent(); @@ -16336,7 +16614,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) { static char buf[64] = ""; SetNextItemWidth(-FLT_MIN); - InputText("##DebugTextEncodingBuf", buf, IM_ARRAYSIZE(buf)); + InputText("##DebugTextEncodingBuf", buf, IM_COUNTOF(buf)); if (buf[0] != 0) DebugTextEncoding(buf); } @@ -16530,7 +16808,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("%d current allocations", info->TotalAllocCount - info->TotalFreeCount); if (SmallButton("GC now")) { g.GcCompactAll = true; } Text("Recent frames with allocations:"); - int buf_size = IM_ARRAYSIZE(info->LastEntriesBuf); + int buf_size = IM_COUNTOF(info->LastEntriesBuf); for (int n = buf_size - 1; n >= 0; n--) { ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[(info->LastEntriesIdx - n + buf_size) % buf_size]; @@ -16553,7 +16831,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("Keys down:"); for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (!IsKeyDown(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); SameLine(); Text("(%.02f)", GetKeyData(key)->DownDuration); } Text("Keys pressed:"); for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (!IsKeyPressed(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); } Text("Keys released:"); for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (!IsKeyReleased(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); } - Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : ""); + Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "Ctrl " : "", io.KeyShift ? "Shift " : "", io.KeyAlt ? "Alt " : "", io.KeySuper ? "Super " : ""); Text("Chars queue:"); for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; SameLine(); Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public. DebugRenderKeyboardPreview(GetWindowDrawList()); Unindent(); @@ -16567,7 +16845,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) else Text("Mouse pos: "); Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y); - int count = IM_ARRAYSIZE(io.MouseDown); + int count = IM_COUNTOF(io.MouseDown); Text("Mouse down:"); for (int i = 0; i < count; i++) if (IsMouseDown(i)) { SameLine(); Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } Text("Mouse clicked:"); for (int i = 0; i < count; i++) if (IsMouseClicked(i)) { SameLine(); Text("b%d (%d)", i, io.MouseClickedCount[i]); } Text("Mouse released:"); for (int i = 0; i < count; i++) if (IsMouseReleased(i)) { SameLine(); Text("b%d", i); } @@ -16698,7 +16976,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (cfg->ShowWindowsBeginOrder && !(window->Flags & ImGuiWindowFlags_ChildWindow)) { char buf[32]; - ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext); + ImFormatString(buf, IM_COUNTOF(buf), "%d", window->BeginOrderWithinContext); float font_size = GetFontSize(); draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255)); draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf); @@ -16849,9 +17127,9 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, con } char texid_desc[30]; - FormatTextureIDForDebugDisplay(texid_desc, IM_ARRAYSIZE(texid_desc), pcmd); + FormatTextureRefForDebugDisplay(texid_desc, IM_COUNTOF(texid_desc), pcmd->TexRef); char buf[300]; - ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d tris, Tex %s, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)", + ImFormatString(buf, IM_COUNTOF(buf), "DrawCmd:%5d tris, Tex %s, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)", pcmd->ElemCount / 3, texid_desc, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf); if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list) @@ -16873,7 +17151,7 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, con } // Display vertex information summary. Hover to get all triangles drawn in wire-frame - ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area); + ImFormatString(buf, IM_COUNTOF(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area); Selectable(buf); if (IsItemHovered() && fg_draw_list) DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, true, false); @@ -16884,7 +17162,7 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, con while (clipper.Step()) for (int prim = clipper.DisplayStart, idx_i = pcmd->IdxOffset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++) { - char* buf_p = buf, * buf_end = buf + IM_ARRAYSIZE(buf); + char* buf_p = buf, * buf_end = buf + IM_COUNTOF(buf); ImVec2 triangle[3]; for (int n = 0; n < 3; n++, idx_i++) { @@ -16958,7 +17236,7 @@ void ImGui::DebugNodeFont(ImFont* font) { ImGuiContext& g = *GImGui; ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; - ImFontAtlas* atlas = font->ContainerAtlas; + ImFontAtlas* atlas = font->OwnerAtlas; bool opened = TreeNode(font, "Font: \"%s\": %d sources(s)", font->GetDebugName(), font->Sources.Size); // Display preview text @@ -17004,17 +17282,22 @@ void ImGui::DebugNodeFont(ImFont* font) #endif char c_str[5]; - Text("Fallback character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->FallbackChar), font->FallbackChar); - Text("Ellipsis character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->EllipsisChar), font->EllipsisChar); + ImTextCharToUtf8(c_str, font->FallbackChar); + Text("Fallback character: '%s' (U+%04X)", c_str, font->FallbackChar); + ImTextCharToUtf8(c_str, font->EllipsisChar); + Text("Ellipsis character: '%s' (U+%04X)", c_str, font->EllipsisChar); for (int src_n = 0; src_n < font->Sources.Size; src_n++) { ImFontConfig* src = font->Sources[src_n]; - if (TreeNode(src, "Input %d: \'%s\', Oversample: %d,%d, PixelSnapH: %d, Offset: (%.1f,%.1f)", - src_n, src->Name, src->OversampleH, src->OversampleV, src->PixelSnapH, src->GlyphOffset.x, src->GlyphOffset.y)) + if (TreeNode(src, "Input %d: \'%s\' [%d], Oversample: %d,%d, PixelSnapH: %d, Offset: (%.1f,%.1f)", + src_n, src->Name, src->FontNo, src->OversampleH, src->OversampleV, src->PixelSnapH, src->GlyphOffset.x, src->GlyphOffset.y)) { const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader; Text("Loader: '%s'", loader->Name ? loader->Name : "N/A"); + + //if (DragFloat("ExtraSizeScale", &src->ExtraSizeScale, 0.01f, 0.10f, 2.0f)) + // ImFontAtlasFontRebuildOutput(atlas, font); #ifdef IMGUI_ENABLE_FREETYPE if (loader->Name != NULL && strcmp(loader->Name, "FreeType") == 0) { @@ -17047,7 +17330,10 @@ void ImGui::DebugNodeFont(ImFont* font) { char utf8_buf[5]; for (unsigned int n = c; n < c_end; n++) - BulletText("Codepoint U+%04X (%s)", n, ImTextCharToUtf8(utf8_buf, n)); + { + ImTextCharToUtf8(utf8_buf, n); + BulletText("Codepoint U+%04X (%s)", n, utf8_buf); + } TreePop(); } TableNextColumn(); @@ -17068,7 +17354,7 @@ void ImGui::DebugNodeFont(ImFont* font) for (int baked_n = 0; baked_n < atlas->Builder->BakedPool.Size; baked_n++) { ImFontBaked* baked = &atlas->Builder->BakedPool[baked_n]; - if (baked->ContainerFont != font) + if (baked->OwnerFont != font) continue; PushID(baked_n); if (TreeNode("Glyphs", "Baked at { %.2fpx, d.%.2f }: %d glyphs%s", baked->Size, baked->RasterizerDensity, baked->Glyphs.Size, (baked->LastUsedFrame < atlas->Builder->FrameCount - 1) ? " *Unused*" : "")) @@ -17159,8 +17445,8 @@ void ImGui::DebugNodeFontGlyph(ImFont* font, const ImFontGlyph* glyph) Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1); if (glyph->PackId >= 0) { - ImTextureRect* r = ImFontAtlasPackGetRect(font->ContainerAtlas, glyph->PackId); - Text("PackId: %d (%dx%d rect at %d,%d)", glyph->PackId, r->w, r->h, r->x, r->y); + ImTextureRect* r = ImFontAtlasPackGetRect(font->OwnerAtlas, glyph->PackId); + Text("PackId: 0x%X (%dx%d rect at %d,%d)", glyph->PackId, r->w, r->h, r->x, r->y); } Text("SourceIdx: %d", glyph->SourceIdx); } @@ -17184,7 +17470,7 @@ void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label) // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings. char buf[256]; char* p = buf; - const char* buf_end = buf + IM_ARRAYSIZE(buf); + const char* buf_end = buf + IM_COUNTOF(buf); const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2); p += ImFormatString(p, buf_end - p, "%s 0x%08X (%d tabs)%s {", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*"); for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++) @@ -17198,7 +17484,7 @@ void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label) if (!is_active) { PopStyleColor(); } if (is_active && IsItemHovered()) { - ImDrawList* draw_list = GetForegroundDrawList(); + ImDrawList* draw_list = GetForegroundDrawList(tab_bar->Window); draw_list->AddRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255)); draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255)); draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255)); @@ -17294,7 +17580,7 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label) } const ImVec2* pr = window->NavPreferredScoringPosRel; for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++) - BulletText("NavPreferredScoringPosRel[%d] = {%.1f,%.1f)", layer, (pr[layer].x == FLT_MAX ? -99999.0f : pr[layer].x), (pr[layer].y == FLT_MAX ? -99999.0f : pr[layer].y)); // Display as 99999.0f so it looks neater. + BulletText("NavPreferredScoringPosRel[%d] = (%.1f,%.1f)", layer, (pr[layer].x == FLT_MAX ? -99999.0f : pr[layer].x), (pr[layer].y == FLT_MAX ? -99999.0f : pr[layer].y)); // Display as 99999.0f so it looks neater. BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); } if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); } @@ -17342,7 +17628,7 @@ void ImGui::DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int wi if (window->ParentWindowInBeginStack != parent_in_begin_stack) continue; char buf[20]; - ImFormatString(buf, IM_ARRAYSIZE(buf), "[%04d] Window", window->BeginOrderWithinContext); + ImFormatString(buf, IM_COUNTOF(buf), "[%04d] Window", window->BeginOrderWithinContext); //BulletText("[%04d] Window '%s'", window->BeginOrderWithinContext, window->Name); DebugNodeWindow(window, buf); TreePush(buf); @@ -17373,14 +17659,23 @@ void ImGui::DebugLogV(const char* fmt, va_list args) g.DebugLogBuf.appendf("[%05d] ", g.FrameCount); g.DebugLogBuf.appendfv(fmt, args); g.DebugLogIndex.append(g.DebugLogBuf.c_str(), old_size, g.DebugLogBuf.size()); + + const char* str = g.DebugLogBuf.begin() + old_size; if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTTY) - IMGUI_DEBUG_PRINTF("%s", g.DebugLogBuf.begin() + old_size); + IMGUI_DEBUG_PRINTF("%s", str); +#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) + if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToDebugger) + { + ::OutputDebugStringA("[imgui] "); + ::OutputDebugStringA(str); + } +#endif #ifdef IMGUI_ENABLE_TEST_ENGINE // IMGUI_TEST_ENGINE_LOG() adds a trailing \n automatically const int new_size = g.DebugLogBuf.size(); const bool trailing_carriage_return = (g.DebugLogBuf[new_size - 1] == '\n'); if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTestEngine) - IMGUI_TEST_ENGINE_LOG("%.*s", new_size - old_size - (trailing_carriage_return ? 1 : 0), g.DebugLogBuf.begin() + old_size); + IMGUI_TEST_ENGINE_LOG("%.*s", new_size - old_size - (trailing_carriage_return ? 1 : 0), str); #endif } @@ -17415,7 +17710,7 @@ static void ShowDebugLogFlag(const char* name, ImGuiDebugLogFlags flags) } else { - ImGui::SetItemTooltip("Hold SHIFT when clicking to enable for 2 frames only (useful for spammy log entries)"); + ImGui::SetItemTooltip("Hold Shift when clicking to enable for 2 frames only (useful for spammy log entries)"); } } @@ -17460,6 +17755,7 @@ void ImGui::ShowDebugLogWindow(bool* p_open) if (BeginPopup("Outputs")) { CheckboxFlags("OutputToTTY", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToTTY); + CheckboxFlags("OutputToDebugger", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToDebugger); #ifndef IMGUI_ENABLE_TEST_ENGINE BeginDisabled(); #endif @@ -17633,111 +17929,157 @@ void ImGui::UpdateDebugToolItemPicker() EndTooltip(); } -// [DEBUG] ID Stack Tool: update queries. Called by NewFrame() -void ImGui::UpdateDebugToolStackQueries() +// Update queries. The steps are: -1: query Stack, >= 0: query each stack item +// We can only perform 1 ID Info query every frame. This is designed so the GetID() tests are cheap and constant-time +static ImGuiID DebugItemPathQuery_UpdateAndGetHookId(ImGuiDebugItemPathQuery* query, ImGuiID id) { - ImGuiContext& g = *GImGui; - ImGuiIDStackTool* tool = &g.DebugIDStackTool; - - // Clear hook when id stack tool is not visible - g.DebugHookIdInfo = 0; - if (g.FrameCount != tool->LastActiveFrame + 1) - return; - - // Update queries. The steps are: -1: query Stack, >= 0: query each stack item - // We can only perform 1 ID Info query every frame. This is designed so the GetID() tests are cheap and constant-time - const ImGuiID query_id = g.HoveredIdPreviousFrame ? g.HoveredIdPreviousFrame : g.ActiveId; - if (tool->QueryId != query_id) + // Update query. Clear hook when no active query + if (query->MainID != id) { - tool->QueryId = query_id; - tool->StackLevel = -1; - tool->Results.resize(0); + query->MainID = id; + query->Step = -1; + query->Complete = false; + query->Results.resize(0); + query->ResultsDescBuf.resize(0); } - if (query_id == 0) - return; + query->Active = false; + if (id == 0) + return 0; // Advance to next stack level when we got our result, or after 2 frames (in case we never get a result) - int stack_level = tool->StackLevel; - if (stack_level >= 0 && stack_level < tool->Results.Size) - if (tool->Results[stack_level].QuerySuccess || tool->Results[stack_level].QueryFrameCount > 2) - tool->StackLevel++; + if (query->Step >= 0 && query->Step < query->Results.Size) + if (query->Results[query->Step].QuerySuccess || query->Results[query->Step].QueryFrameCount > 2) + query->Step++; - // Update hook - stack_level = tool->StackLevel; - if (stack_level == -1) - g.DebugHookIdInfo = query_id; - if (stack_level >= 0 && stack_level < tool->Results.Size) + // Update status and hook + query->Complete = (query->Step == query->Results.Size); + if (query->Step == -1) + { + query->Active = true; + return id; + } + else if (query->Step >= 0 && query->Step < query->Results.Size) { - g.DebugHookIdInfo = tool->Results[stack_level].ID; - tool->Results[stack_level].QueryFrameCount++; + query->Results[query->Step].QueryFrameCount++; + query->Active = true; + return query->Results[query->Step].ID; } + return 0; +} + +// [DEBUG] ID Stack Tool: update query. Called by NewFrame() +void ImGui::UpdateDebugToolItemPathQuery() +{ + ImGuiContext& g = *GImGui; + ImGuiID id = 0; + if (g.DebugIDStackTool.LastActiveFrame + 1 == g.FrameCount) + id = g.HoveredIdPreviousFrame ? g.HoveredIdPreviousFrame : g.ActiveId; + g.DebugHookIdInfoId = DebugItemPathQuery_UpdateAndGetHookId(&g.DebugItemPathQuery, id); } // [DEBUG] ID Stack tool: hooks called by GetID() family functions void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end) { ImGuiContext& g = *GImGui; + ImGuiDebugItemPathQuery* query = &g.DebugItemPathQuery; + if (query->Active == false) + { + IM_ASSERT(id == 0); + return; + } ImGuiWindow* window = g.CurrentWindow; - ImGuiIDStackTool* tool = &g.DebugIDStackTool; - // Step 0: stack query + // Step -1: stack query // This assumes that the ID was computed with the current ID stack, which tends to be the case for our widget. - if (tool->StackLevel == -1) + if (query->Step == -1) { - tool->StackLevel++; - tool->Results.resize(window->IDStack.Size + 1, ImGuiStackLevelInfo()); + IM_ASSERT(query->Results.Size == 0); + query->Step++; + query->Results.resize(window->IDStack.Size + 1, ImGuiStackLevelInfo()); for (int n = 0; n < window->IDStack.Size + 1; n++) - tool->Results[n].ID = (n < window->IDStack.Size) ? window->IDStack[n] : id; + query->Results[n].ID = (n < window->IDStack.Size) ? window->IDStack[n] : id; return; } - // Step 1+: query for individual level - IM_ASSERT(tool->StackLevel >= 0); - if (tool->StackLevel != window->IDStack.Size) + // Step 0+: query for individual level + IM_ASSERT(query->Step >= 0); + if (query->Step != window->IDStack.Size) return; - ImGuiStackLevelInfo* info = &tool->Results[tool->StackLevel]; + ImGuiStackLevelInfo* info = &query->Results[query->Step]; IM_ASSERT(info->ID == id && info->QueryFrameCount > 0); - switch (data_type) + if (info->DescOffset == -1) { - case ImGuiDataType_S32: - ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "%d", (int)(intptr_t)data_id); - break; - case ImGuiDataType_String: - ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "%.*s", data_id_end ? (int)((const char*)data_id_end - (const char*)data_id) : (int)ImStrlen((const char*)data_id), (const char*)data_id); - break; - case ImGuiDataType_Pointer: - ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "(void*)0x%p", data_id); - break; - case ImGuiDataType_ID: - if (info->Desc[0] != 0) // PushOverrideID() is often used to avoid hashing twice, which would lead to 2 calls to DebugHookIdInfo(). We prioritize the first one. - return; - ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "0x%08X [override]", id); - break; - default: - IM_ASSERT(0); + const char* result = NULL; + const char* result_end = NULL; + switch (data_type) + { + case ImGuiDataType_S32: + ImFormatStringToTempBuffer(&result, &result_end, "%d", (int)(intptr_t)data_id); + break; + case ImGuiDataType_String: + ImFormatStringToTempBuffer(&result, &result_end, "%.*s", data_id_end ? (int)((const char*)data_id_end - (const char*)data_id) : (int)ImStrlen((const char*)data_id), (const char*)data_id); + break; + case ImGuiDataType_Pointer: + ImFormatStringToTempBuffer(&result, &result_end, "(void*)0x%p", data_id); + break; + case ImGuiDataType_ID: + // PushOverrideID() is often used to avoid hashing twice, which would lead to 2 calls to DebugHookIdInfo(). We prioritize the first one. + ImFormatStringToTempBuffer(&result, &result_end, "0x%08X [override]", id); + break; + default: + IM_ASSERT(0); + } + info->DescOffset = query->ResultsDescBuf.size(); + query->ResultsDescBuf.append(result, result_end + 1); // Include zero terminator } info->QuerySuccess = true; - info->DataType = data_type; + if (info->DataType == -1) + info->DataType = (ImS8)data_type; } -static int StackToolFormatLevelInfo(ImGuiIDStackTool* tool, int n, bool format_for_ui, char* buf, size_t buf_size) +static int DebugItemPathQuery_FormatLevelInfo(ImGuiDebugItemPathQuery* query, int n, bool format_for_ui, char* buf, size_t buf_size) { - ImGuiStackLevelInfo* info = &tool->Results[n]; - ImGuiWindow* window = (info->Desc[0] == 0 && n == 0) ? ImGui::FindWindowByID(info->ID) : NULL; - if (window) // Source: window name (because the root ID don't call GetID() and so doesn't get hooked) - return ImFormatString(buf, buf_size, format_for_ui ? "\"%s\" [window]" : "%s", window->Name); - if (info->QuerySuccess) // Source: GetID() hooks (prioritize over ItemInfo() because we frequently use patterns like: PushID(str), Button("") where they both have same id) - return ImFormatString(buf, buf_size, (format_for_ui && info->DataType == ImGuiDataType_String) ? "\"%s\"" : "%s", info->Desc); - if (tool->StackLevel < tool->Results.Size) // Only start using fallback below when all queries are done, so during queries we don't flickering ??? markers. + ImGuiStackLevelInfo* info = &query->Results[n]; + ImGuiWindow* window = (info->DescOffset == -1 && n == 0) ? ImGui::FindWindowByID(info->ID) : NULL; + if (window) // Source: window name (because the root ID don't call GetID() and so doesn't get hooked) + return ImFormatString(buf, buf_size, format_for_ui ? "\"%s\" [window]" : "%s", ImHashSkipUncontributingPrefix(window->Name)); + if (info->QuerySuccess) // Source: GetID() hooks (prioritize over ItemInfo() because we frequently use patterns like: PushID(str), Button("") where they both have same id) + return ImFormatString(buf, buf_size, (format_for_ui && info->DataType == ImGuiDataType_String) ? "\"%s\"" : "%s", ImHashSkipUncontributingPrefix(&query->ResultsDescBuf.Buf[info->DescOffset])); + if (query->Step < query->Results.Size) // Only start using fallback below when all queries are done, so during queries we don't flickering ??? markers. return (*buf = 0); #ifdef IMGUI_ENABLE_TEST_ENGINE - if (const char* label = ImGuiTestEngine_FindItemDebugLabel(GImGui, info->ID)) // Source: ImGuiTestEngine's ItemInfo() - return ImFormatString(buf, buf_size, format_for_ui ? "??? \"%s\"" : "%s", label); + if (const char* label = ImGuiTestEngine_FindItemDebugLabel(GImGui, info->ID)) // Source: ImGuiTestEngine's ItemInfo() + return ImFormatString(buf, buf_size, format_for_ui ? "??? \"%s\"" : "%s", ImHashSkipUncontributingPrefix(label)); #endif return ImFormatString(buf, buf_size, "???"); } +static const char* DebugItemPathQuery_GetResultAsPath(ImGuiDebugItemPathQuery* query, bool hex_encode_non_ascii_chars) +{ + ImGuiTextBuffer* buf = &query->ResultPathBuf; + buf->resize(0); + for (int stack_n = 0; stack_n < query->Results.Size; stack_n++) + { + char level_desc[256]; + DebugItemPathQuery_FormatLevelInfo(query, stack_n, false, level_desc, IM_COUNTOF(level_desc)); + buf->append(stack_n == 0 ? "//" : "/"); + for (const char* p = level_desc; *p != 0; ) + { + unsigned int c; + const char* p_next = p + ImTextCharFromUtf8(&c, p, NULL); + if (c == '/') + buf->append("\\"); + if (c < 256 || !hex_encode_non_ascii_chars) + buf->append(p, p_next); + else for (; p < p_next; p++) + buf->appendf("\\x%02x", (unsigned char)*p); + p = p_next; + } + } + return buf->c_str(); +} + // ID Stack Tool: Display UI void ImGui::ShowIDStackToolWindow(bool* p_open) { @@ -17750,66 +18092,54 @@ void ImGui::ShowIDStackToolWindow(bool* p_open) return; } - // Display hovered/active status + ImGuiDebugItemPathQuery* query = &g.DebugItemPathQuery; ImGuiIDStackTool* tool = &g.DebugIDStackTool; - - // Build and display path - tool->ResultPathBuf.resize(0); - for (int stack_n = 0; stack_n < tool->Results.Size; stack_n++) - { - char level_desc[256]; - StackToolFormatLevelInfo(tool, stack_n, false, level_desc, IM_ARRAYSIZE(level_desc)); - tool->ResultPathBuf.append(stack_n == 0 ? "//" : "/"); - for (int n = 0; level_desc[n]; n++) - { - if (level_desc[n] == '/') - tool->ResultPathBuf.append("\\"); - tool->ResultPathBuf.append(level_desc + n, level_desc + n + 1); - } - } - Text("0x%08X", tool->QueryId); + tool->LastActiveFrame = g.FrameCount; + const char* result_path = DebugItemPathQuery_GetResultAsPath(query, tool->OptHexEncodeNonAsciiChars); + Text("0x%08X", query->MainID); SameLine(); MetricsHelpMarker("Hover an item with the mouse to display elements of the ID Stack leading to the item's final ID.\nEach level of the stack correspond to a PushID() call.\nAll levels of the stack are hashed together to make the final ID of a widget (ID displayed at the bottom level of the stack).\nRead FAQ entry about the ID stack for details."); - // CTRL+C to copy path + // Ctrl+C to copy path const float time_since_copy = (float)g.Time - tool->CopyToClipboardLastTime; + PushStyleVarY(ImGuiStyleVar_FramePadding, 0.0f); + Checkbox("Hex-encode non-ASCII", &tool->OptHexEncodeNonAsciiChars); SameLine(); - PushStyleVarY(ImGuiStyleVar_FramePadding, 0.0f); Checkbox("Ctrl+C: copy path", &tool->CopyToClipboardOnCtrlC); PopStyleVar(); + 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*"); - if (tool->CopyToClipboardOnCtrlC && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused)) + if (tool->OptCopyToClipboardOnCtrlC && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused)) { tool->CopyToClipboardLastTime = (float)g.Time; - SetClipboardText(tool->ResultPathBuf.c_str()); + SetClipboardText(result_path); } - Text("- Path \"%s\"", tool->ResultPathBuf.c_str()); + Text("- Path \"%s\"", query->Complete ? result_path : ""); #ifdef IMGUI_ENABLE_TEST_ENGINE - Text("- Label \"%s\"", tool->QueryId ? ImGuiTestEngine_FindItemDebugLabel(&g, tool->QueryId) : ""); + Text("- Label \"%s\"", query->MainID ? ImGuiTestEngine_FindItemDebugLabel(&g, query->MainID) : ""); #endif - Separator(); // Display decorated stack - tool->LastActiveFrame = g.FrameCount; - if (tool->Results.Size > 0 && BeginTable("##table", 3, ImGuiTableFlags_Borders)) + if (query->Results.Size > 0 && BeginTable("##table", 3, ImGuiTableFlags_Borders)) { const float id_width = CalcTextSize("0xDDDDDDDD").x; TableSetupColumn("Seed", ImGuiTableColumnFlags_WidthFixed, id_width); TableSetupColumn("PushID", ImGuiTableColumnFlags_WidthStretch); TableSetupColumn("Result", ImGuiTableColumnFlags_WidthFixed, id_width); TableHeadersRow(); - for (int n = 0; n < tool->Results.Size; n++) + for (int n = 0; n < query->Results.Size; n++) { - ImGuiStackLevelInfo* info = &tool->Results[n]; + ImGuiStackLevelInfo* info = &query->Results[n]; TableNextColumn(); - Text("0x%08X", (n > 0) ? tool->Results[n - 1].ID : 0); + Text("0x%08X", (n > 0) ? query->Results[n - 1].ID : 0); TableNextColumn(); - StackToolFormatLevelInfo(tool, n, true, g.TempBuffer.Data, g.TempBuffer.Size); + DebugItemPathQuery_FormatLevelInfo(query, n, true, g.TempBuffer.Data, g.TempBuffer.Size); TextUnformatted(g.TempBuffer.Data); TableNextColumn(); Text("0x%08X", info->ID); - if (n == tool->Results.Size - 1) + if (n == query->Results.Size - 1) TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_Header)); } EndTable(); @@ -17852,7 +18182,7 @@ void ImGui::ShowFontSelector(const char* label) for (ImFont* font : io.Fonts->Fonts) { PushID((void*)font); - if (Selectable(font->GetDebugName(), font == font_current)) + if (Selectable(font->GetDebugName(), font == font_current, ImGuiSelectableFlags_SelectOnNav)) io.FontDefault = font; if (font == font_current) SetItemDefaultFocus(); diff --git a/dep/imgui/src/imgui_demo.cpp b/dep/imgui/src/imgui_demo.cpp index 25cbd6a0f..258f9283e 100644 --- a/dep/imgui/src/imgui_demo.cpp +++ b/dep/imgui/src/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.0 +// dear imgui, v1.92.6 WIP // (demo code) // Help: @@ -59,9 +59,9 @@ // Because we can't assume anything about your support of maths operators, we cannot use them in imgui_demo.cpp. // Navigating this file: -// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. -// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments. -// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments. +// - In Visual Studio: Ctrl+Comma ("Edit.GoToAll") can follow symbols inside comments, whereas Ctrl+F12 ("Edit.GoToImplementation") cannot. +// - In Visual Studio w/ Visual Assist installed: Alt+G ("VAssistX.GoToImplementation") can also follow symbols inside comments. +// - In VS Code, CLion, etc.: Ctrl+Click can follow symbols inside comments. // - You can search/grep for all sections listed in the index to find the section. /* @@ -288,7 +288,7 @@ extern ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback; extern void* GImGuiDemoMarkerCallbackUserData; ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback = NULL; void* GImGuiDemoMarkerCallbackUserData = NULL; -#define IMGUI_DEMO_MARKER(section) do { if (GImGuiDemoMarkerCallback != NULL) GImGuiDemoMarkerCallback(__FILE__, __LINE__, section, GImGuiDemoMarkerCallbackUserData); } while (0) +#define IMGUI_DEMO_MARKER(section) do { if (GImGuiDemoMarkerCallback != NULL) GImGuiDemoMarkerCallback("imgui_demo.cpp", __LINE__, section, GImGuiDemoMarkerCallbackUserData); } while (0) //----------------------------------------------------------------------------- // [SECTION] Demo Window / ShowDemoWindow() @@ -510,7 +510,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::SameLine(); HelpMarker("Enable resizing of windows from their edges and from the lower-left corner.\nThis requires ImGuiBackendFlags_HasMouseCursors for better mouse cursor feedback."); ImGui::Checkbox("io.ConfigWindowsMoveFromTitleBarOnly", &io.ConfigWindowsMoveFromTitleBarOnly); ImGui::Checkbox("io.ConfigWindowsCopyContentsWithCtrlC", &io.ConfigWindowsCopyContentsWithCtrlC); // [EXPERIMENTAL] - ImGui::SameLine(); HelpMarker("*EXPERIMENTAL* CTRL+C copy the contents of focused window into the clipboard.\n\nExperimental because:\n- (1) has known issues with nested Begin/End pairs.\n- (2) text output quality varies.\n- (3) text output is in submission order rather than spatial order."); + ImGui::SameLine(); HelpMarker("*EXPERIMENTAL* Ctrl+C copy the contents of focused window into the clipboard.\n\nExperimental because:\n- (1) has known issues with nested Begin/End pairs.\n- (2) text output quality varies.\n- (3) text output is in submission order rather than spatial order."); ImGui::Checkbox("io.ConfigScrollbarScrollByPage", &io.ConfigScrollbarScrollByPage); ImGui::SameLine(); HelpMarker("Enable scrolling page by page when clicking outside the scrollbar grab.\nWhen disabled, always scroll to clicked location.\nWhen enabled, Shift+Click scrolls to clicked location."); @@ -764,7 +764,7 @@ static const ExampleMemberInfo ExampleTreeNodeMemberInfos[] static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, ExampleTreeNode* parent) { ExampleTreeNode* node = IM_NEW(ExampleTreeNode); - snprintf(node->Name, IM_ARRAYSIZE(node->Name), "%s", name); + snprintf(node->Name, IM_COUNTOF(node->Name), "%s", name); node->UID = uid; node->Parent = parent; node->IndexInParent = parent ? (unsigned short)parent->Childs.Size : 0; @@ -790,19 +790,19 @@ static ExampleTreeNode* ExampleTree_CreateDemoTree() int uid = 0; ExampleTreeNode* node_L0 = ExampleTree_CreateNode("", ++uid, NULL); const int root_items_multiplier = 2; - for (int idx_L0 = 0; idx_L0 < IM_ARRAYSIZE(root_names) * root_items_multiplier; idx_L0++) + for (int idx_L0 = 0; idx_L0 < IM_COUNTOF(root_names) * root_items_multiplier; idx_L0++) { - snprintf(name_buf, IM_ARRAYSIZE(name_buf), "%s %d", root_names[idx_L0 / root_items_multiplier], idx_L0 % root_items_multiplier); + snprintf(name_buf, IM_COUNTOF(name_buf), "%s %d", root_names[idx_L0 / root_items_multiplier], idx_L0 % root_items_multiplier); ExampleTreeNode* node_L1 = ExampleTree_CreateNode(name_buf, ++uid, node_L0); const int number_of_childs = (int)strlen(node_L1->Name); for (int idx_L1 = 0; idx_L1 < number_of_childs; idx_L1++) { - snprintf(name_buf, IM_ARRAYSIZE(name_buf), "Child %d", idx_L1); + snprintf(name_buf, IM_COUNTOF(name_buf), "Child %d", idx_L1); ExampleTreeNode* node_L2 = ExampleTree_CreateNode(name_buf, ++uid, node_L1); node_L2->HasData = true; if (idx_L1 == 0) { - snprintf(name_buf, IM_ARRAYSIZE(name_buf), "Sub-child %d", 0); + snprintf(name_buf, IM_COUNTOF(name_buf), "Sub-child %d", 0); ExampleTreeNode* node_L3 = ExampleTree_CreateNode(name_buf, ++uid, node_L2); node_L3->HasData = true; } @@ -887,26 +887,27 @@ static void DemoWindowWidgetsBasic() ImGui::SeparatorText("Inputs"); { - // To wire InputText() with std::string or any other custom string type, - // see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file. + // If you want to use InputText() with std::string or any custom dynamic string type: + // - For std::string: use the wrapper in misc/cpp/imgui_stdlib.h/.cpp + // - Otherwise, see the 'Dear ImGui Demo->Widgets->Text Input->Resize Callback' for using ImGuiInputTextFlags_CallbackResize. IMGUI_DEMO_MARKER("Widgets/Basic/InputText"); static char str0[128] = "Hello, world!"; - ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0)); + ImGui::InputText("input text", str0, IM_COUNTOF(str0)); ImGui::SameLine(); HelpMarker( "USER:\n" - "Hold SHIFT or use mouse to select text.\n" - "CTRL+Left/Right to word jump.\n" - "CTRL+A or Double-Click to select all.\n" - "CTRL+X,CTRL+C,CTRL+V for clipboard.\n" - "CTRL+Z to undo, CTRL+Y/CTRL+SHIFT+Z to redo.\n" - "ESCAPE to revert.\n\n" + "Hold Shift or use mouse to select text.\n" + "Ctrl+Left/Right to word jump.\n" + "Ctrl+A or Double-Click to select all.\n" + "Ctrl+X,Ctrl+C,Ctrl+V for clipboard.\n" + "Ctrl+Z to undo, Ctrl+Y/Ctrl+Shift+Z to redo.\n" + "Escape to revert.\n\n" "PROGRAMMER:\n" "You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputText() " "to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example (this is not demonstrated " "in imgui_demo.cpp)."); static char str1[128] = ""; - ImGui::InputTextWithHint("input text (w/ hint)", "enter text here", str1, IM_ARRAYSIZE(str1)); + ImGui::InputTextWithHint("input text (w/ hint)", "enter text here", str1, IM_COUNTOF(str1)); IMGUI_DEMO_MARKER("Widgets/Basic/InputInt, InputFloat"); static int i0 = 123; @@ -936,8 +937,8 @@ static void DemoWindowWidgetsBasic() ImGui::DragInt("drag int", &i1, 1); ImGui::SameLine(); HelpMarker( "Click and drag to edit value.\n" - "Hold SHIFT/ALT for faster/slower edit.\n" - "Double-click or CTRL+click to input value."); + "Hold Shift/Alt for faster/slower edit.\n" + "Double-Click or Ctrl+Click to input value."); ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%d%%", ImGuiSliderFlags_AlwaysClamp); ImGui::DragInt("drag int wrap 100..200", &i3, 1, 100, 200, "%d", ImGuiSliderFlags_WrapAround); @@ -953,7 +954,7 @@ static void DemoWindowWidgetsBasic() IMGUI_DEMO_MARKER("Widgets/Basic/SliderInt, SliderFloat"); static int i1 = 0; ImGui::SliderInt("slider int", &i1, -1, 3); - ImGui::SameLine(); HelpMarker("CTRL+click to input value."); + ImGui::SameLine(); HelpMarker("Ctrl+Click to input value."); static float f1 = 0.123f, f2 = 0.0f; ImGui::SliderFloat("slider float", &f1, 0.0f, 1.0f, "ratio = %.3f"); @@ -971,7 +972,7 @@ static void DemoWindowWidgetsBasic() static int elem = Element_Fire; const char* elems_names[Element_COUNT] = { "Fire", "Earth", "Air", "Water" }; const char* elem_name = (elem >= 0 && elem < Element_COUNT) ? elems_names[elem] : "Unknown"; - ImGui::SliderInt("slider enum", &elem, 0, Element_COUNT - 1, elem_name); // Use ImGuiSliderFlags_NoInput flag to disable CTRL+Click here. + ImGui::SliderInt("slider enum", &elem, 0, Element_COUNT - 1, elem_name); // Use ImGuiSliderFlags_NoInput flag to disable Ctrl+Click here. ImGui::SameLine(); HelpMarker("Using the format string parameter to display a name instead of the underlying integer."); } @@ -985,8 +986,8 @@ static void DemoWindowWidgetsBasic() ImGui::SameLine(); HelpMarker( "Click on the color square to open a color picker.\n" "Click and hold to use drag and drop.\n" - "Right-click on the color square to show options.\n" - "CTRL+click on individual component to input value.\n"); + "Right-Click on the color square to show options.\n" + "Ctrl+Click on individual component to input value.\n"); ImGui::ColorEdit4("color 2", col2); } @@ -997,7 +998,7 @@ static void DemoWindowWidgetsBasic() IMGUI_DEMO_MARKER("Widgets/Basic/Combo"); const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIIIIII", "JJJJ", "KKKKKKK" }; static int item_current = 0; - ImGui::Combo("combo", &item_current, items, IM_ARRAYSIZE(items)); + ImGui::Combo("combo", &item_current, items, IM_COUNTOF(items)); ImGui::SameLine(); HelpMarker( "Using the simplified one-liner Combo API here.\n" "Refer to the \"Combo\" section below for an explanation of how to use the more flexible and general BeginCombo/EndCombo API."); @@ -1009,7 +1010,7 @@ static void DemoWindowWidgetsBasic() IMGUI_DEMO_MARKER("Widgets/Basic/ListBox"); const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" }; static int item_current = 1; - ImGui::ListBox("listbox", &item_current, items, IM_ARRAYSIZE(items), 4); + ImGui::ListBox("listbox", &item_current, items, IM_COUNTOF(items), 4); ImGui::SameLine(); HelpMarker( "Using the simplified one-liner ListBox API here.\n" "Refer to the \"List boxes\" section below for an explanation of how to use the more flexible and general BeginListBox/EndListBox API."); @@ -1095,8 +1096,9 @@ static void DemoWindowWidgetsColorAndPickers() ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaOpaque", &base_flags, ImGuiColorEditFlags_AlphaOpaque); ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaNoBg", &base_flags, ImGuiColorEditFlags_AlphaNoBg); ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaPreviewHalf", &base_flags, ImGuiColorEditFlags_AlphaPreviewHalf); - ImGui::CheckboxFlags("ImGuiColorEditFlags_NoDragDrop", &base_flags, ImGuiColorEditFlags_NoDragDrop); ImGui::CheckboxFlags("ImGuiColorEditFlags_NoOptions", &base_flags, ImGuiColorEditFlags_NoOptions); ImGui::SameLine(); HelpMarker("Right-click on the individual color widget to show options."); + ImGui::CheckboxFlags("ImGuiColorEditFlags_NoDragDrop", &base_flags, ImGuiColorEditFlags_NoDragDrop); + ImGui::CheckboxFlags("ImGuiColorEditFlags_NoColorMarkers", &base_flags, ImGuiColorEditFlags_NoColorMarkers); ImGui::CheckboxFlags("ImGuiColorEditFlags_HDR", &base_flags, ImGuiColorEditFlags_HDR); ImGui::SameLine(); HelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets."); IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit"); @@ -1104,7 +1106,7 @@ static void DemoWindowWidgetsColorAndPickers() ImGui::Text("Color widget:"); ImGui::SameLine(); HelpMarker( "Click on the color square to open a color picker.\n" - "CTRL+click on individual component to input value.\n"); + "Ctrl+Click on individual component to input value.\n"); ImGui::ColorEdit3("MyColor##1", (float*)&color, base_flags); IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (HSV, with Alpha)"); @@ -1131,7 +1133,7 @@ static void DemoWindowWidgetsColorAndPickers() static ImVec4 saved_palette[32] = {}; if (saved_palette_init) { - for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++) + for (int n = 0; n < IM_COUNTOF(saved_palette); n++) { ImGui::ColorConvertHSVtoRGB(n / 31.0f, 0.8f, 0.8f, saved_palette[n].x, saved_palette[n].y, saved_palette[n].z); @@ -1164,7 +1166,7 @@ static void DemoWindowWidgetsColorAndPickers() color = backup_color; ImGui::Separator(); ImGui::Text("Palette"); - for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++) + for (int n = 0; n < IM_COUNTOF(saved_palette); n++) { ImGui::PushID(n); if ((n % 8) != 0) @@ -1317,7 +1319,7 @@ static void DemoWindowWidgetsComboBoxes() const char* combo_preview_value = items[item_selected_idx]; if (ImGui::BeginCombo("combo 1", combo_preview_value, flags)) { - for (int n = 0; n < IM_ARRAYSIZE(items); n++) + for (int n = 0; n < IM_COUNTOF(items); n++) { const bool is_selected = (item_selected_idx == n); if (ImGui::Selectable(items[n], is_selected)) @@ -1343,7 +1345,7 @@ static void DemoWindowWidgetsComboBoxes() ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F); filter.Draw("##Filter", -FLT_MIN); - for (int n = 0; n < IM_ARRAYSIZE(items); n++) + for (int n = 0; n < IM_COUNTOF(items); n++) { const bool is_selected = (item_selected_idx == n); if (filter.PassFilter(items[n])) @@ -1365,11 +1367,11 @@ static void DemoWindowWidgetsComboBoxes() // Simplified one-liner Combo() using an array of const char* // This is not very useful (may obsolete): prefer using BeginCombo()/EndCombo() for full control. static int item_current_3 = -1; // If the selection isn't within 0..count, Combo won't display a preview - ImGui::Combo("combo 4 (array)", &item_current_3, items, IM_ARRAYSIZE(items)); + ImGui::Combo("combo 4 (array)", &item_current_3, items, IM_COUNTOF(items)); // Simplified one-liner Combo() using an accessor function static int item_current_4 = 0; - ImGui::Combo("combo 5 (function)", &item_current_4, [](void* data, int n) { return ((const char**)data)[n]; }, items, IM_ARRAYSIZE(items)); + ImGui::Combo("combo 5 (function)", &item_current_4, [](void* data, int n) { return ((const char**)data)[n]; }, items, IM_COUNTOF(items)); ImGui::TreePop(); } @@ -1436,7 +1438,7 @@ static void DemoWindowWidgetsDataTypes() ImGui::Checkbox("Clamp integers to 0..50", &drag_clamp); ImGui::SameLine(); HelpMarker( "As with every widget in dear imgui, we never modify values unless there is a user interaction.\n" - "You can override the clamping limits by using CTRL+Click to input a value."); + "You can override the clamping limits by using Ctrl+Click to input a value."); ImGui::DragScalar("drag s8", ImGuiDataType_S8, &s8_v, drag_speed, drag_clamp ? &s8_zero : NULL, drag_clamp ? &s8_fifty : NULL); ImGui::DragScalar("drag u8", ImGuiDataType_U8, &u8_v, drag_speed, drag_clamp ? &u8_zero : NULL, drag_clamp ? &u8_fifty : NULL, "%u ms"); ImGui::DragScalar("drag s16", ImGuiDataType_S16, &s16_v, drag_speed, drag_clamp ? &s16_zero : NULL, drag_clamp ? &s16_fifty : NULL); @@ -1568,7 +1570,7 @@ static void DemoWindowWidgetsDragAndDrop() "Brianna", "Barry", "Bernard", "Bibi", "Blaine", "Bryn" }; - for (int n = 0; n < IM_ARRAYSIZE(names); n++) + for (int n = 0; n < IM_COUNTOF(names); n++) { ImGui::PushID(n); if ((n % 3) != 0) @@ -1630,7 +1632,7 @@ static void DemoWindowWidgetsDragAndDrop() "We don't use the drag and drop api at all here! " "Instead we query when the item is held but not hovered, and order items accordingly."); static const char* item_names[] = { "Item One", "Item Two", "Item Three", "Item Four", "Item Five" }; - for (int n = 0; n < IM_ARRAYSIZE(item_names); n++) + for (int n = 0; n < IM_COUNTOF(item_names); n++) { const char* item = item_names[n]; ImGui::Selectable(item); @@ -1638,7 +1640,7 @@ static void DemoWindowWidgetsDragAndDrop() if (ImGui::IsItemActive() && !ImGui::IsItemHovered()) { int n_next = n + (ImGui::GetMouseDragDelta(0).y < 0.f ? -1 : 1); - if (n_next >= 0 && n_next < IM_ARRAYSIZE(item_names)) + if (n_next >= 0 && n_next < IM_COUNTOF(item_names)) { item_names[n] = item_names[n_next]; item_names[n_next] = item; @@ -1696,7 +1698,7 @@ static void DemoWindowWidgetsDragsAndSliders() static ImGuiSliderFlags flags = ImGuiSliderFlags_None; ImGui::CheckboxFlags("ImGuiSliderFlags_AlwaysClamp", &flags, ImGuiSliderFlags_AlwaysClamp); ImGui::CheckboxFlags("ImGuiSliderFlags_ClampOnInput", &flags, ImGuiSliderFlags_ClampOnInput); - ImGui::SameLine(); HelpMarker("Clamp value to min/max bounds when input manually with CTRL+Click. By default CTRL+Click allows going out of bounds."); + ImGui::SameLine(); HelpMarker("Clamp value to min/max bounds when input manually with Ctrl+Click. By default Ctrl+Click allows going out of bounds."); ImGui::CheckboxFlags("ImGuiSliderFlags_ClampZeroRange", &flags, ImGuiSliderFlags_ClampZeroRange); ImGui::SameLine(); HelpMarker("Clamp even if min==max==0.0f. Otherwise DragXXX functions don't clamp."); ImGui::CheckboxFlags("ImGuiSliderFlags_Logarithmic", &flags, ImGuiSliderFlags_Logarithmic); @@ -1704,14 +1706,17 @@ static void DemoWindowWidgetsDragsAndSliders() ImGui::CheckboxFlags("ImGuiSliderFlags_NoRoundToFormat", &flags, ImGuiSliderFlags_NoRoundToFormat); ImGui::SameLine(); HelpMarker("Disable rounding underlying value to match precision of the format string (e.g. %.3f values are rounded to those 3 digits)."); ImGui::CheckboxFlags("ImGuiSliderFlags_NoInput", &flags, ImGuiSliderFlags_NoInput); - ImGui::SameLine(); HelpMarker("Disable CTRL+Click or Enter key allowing to input text directly into the widget."); + ImGui::SameLine(); HelpMarker("Disable Ctrl+Click or Enter key allowing to input text directly into the widget."); ImGui::CheckboxFlags("ImGuiSliderFlags_NoSpeedTweaks", &flags, ImGuiSliderFlags_NoSpeedTweaks); ImGui::SameLine(); HelpMarker("Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic."); ImGui::CheckboxFlags("ImGuiSliderFlags_WrapAround", &flags, ImGuiSliderFlags_WrapAround); ImGui::SameLine(); HelpMarker("Enable wrapping around from max to min and from min to max (only supported by DragXXX() functions)"); + ImGui::CheckboxFlags("ImGuiSliderFlags_ColorMarkers", &flags, ImGuiSliderFlags_ColorMarkers); + //ImGui::CheckboxFlags("ImGuiSliderFlags_ColorMarkersG", &flags, 1 << ImGuiSliderFlags_ColorMarkersIndexShift_); // Not explicitly documented but possible. // Drags static float drag_f = 0.5f; + static float drag_f4[4]; static int drag_i = 50; ImGui::Text("Underlying float value: %f", drag_f); ImGui::DragFloat("DragFloat (0 -> 1)", &drag_f, 0.005f, 0.0f, 1.0f, "%.3f", flags); @@ -1721,14 +1726,17 @@ static void DemoWindowWidgetsDragsAndSliders() //ImGui::DragFloat("DragFloat (0 -> 0)", &drag_f, 0.005f, 0.0f, 0.0f, "%.3f", flags); // To test ClampZeroRange //ImGui::DragFloat("DragFloat (100 -> 100)", &drag_f, 0.005f, 100.0f, 100.0f, "%.3f", flags); ImGui::DragInt("DragInt (0 -> 100)", &drag_i, 0.5f, 0, 100, "%d", flags); + ImGui::DragFloat4("DragFloat4 (0 -> 1)", drag_f4, 0.005f, 0.0f, 1.0f, "%.3f", flags); // Multi-component item, mostly here to document the effect of ImGuiSliderFlags_ColorMarkers. // Sliders static float slider_f = 0.5f; + static float slider_f4[4]; static int slider_i = 50; - const ImGuiSliderFlags flags_for_sliders = flags & ~ImGuiSliderFlags_WrapAround; + const ImGuiSliderFlags flags_for_sliders = (flags & ~ImGuiSliderFlags_WrapAround); ImGui::Text("Underlying float value: %f", slider_f); ImGui::SliderFloat("SliderFloat (0 -> 1)", &slider_f, 0.0f, 1.0f, "%.3f", flags_for_sliders); ImGui::SliderInt("SliderInt (0 -> 100)", &slider_i, 0, 100, "%d", flags_for_sliders); + ImGui::SliderFloat4("SliderFloat4 (0 -> 1)", slider_f4, 0.0f, 1.0f, "%.3f", flags); // Multi-component item, mostly here to document the effect of ImGuiSliderFlags_ColorMarkers. ImGui::TreePop(); } @@ -1872,7 +1880,7 @@ static void DemoWindowWidgetsListBoxes() if (ImGui::BeginListBox("listbox 1")) { - for (int n = 0; n < IM_ARRAYSIZE(items); n++) + for (int n = 0; n < IM_COUNTOF(items); n++) { const bool is_selected = (item_selected_idx == n); if (ImGui::Selectable(items[n], is_selected)) @@ -1893,7 +1901,7 @@ static void DemoWindowWidgetsListBoxes() ImGui::Text("Full-width:"); if (ImGui::BeginListBox("##listbox 2", ImVec2(-FLT_MIN, 5 * ImGui::GetTextLineHeightWithSpacing()))) { - for (int n = 0; n < IM_ARRAYSIZE(items); n++) + for (int n = 0; n < IM_COUNTOF(items); n++) { bool is_selected = (item_selected_idx == n); ImGuiSelectableFlags flags = (item_highlighted_idx == n) ? ImGuiSelectableFlags_Highlight : 0; @@ -1979,8 +1987,8 @@ static void DemoWindowWidgetsPlotting() // Plot as lines and plot as histogram static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; - ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr)); - ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0, 80.0f)); + ImGui::PlotLines("Frame Times", arr, IM_COUNTOF(arr)); + ImGui::PlotHistogram("Histogram", arr, IM_COUNTOF(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0, 80.0f)); //ImGui::SameLine(); HelpMarker("Consider using ImPlot instead!"); // Fill an array of contiguous float values to plot @@ -1995,7 +2003,7 @@ static void DemoWindowWidgetsPlotting() { static float phase = 0.0f; values[values_offset] = cosf(phase); - values_offset = (values_offset + 1) % IM_ARRAYSIZE(values); + values_offset = (values_offset + 1) % IM_COUNTOF(values); phase += 0.10f * values_offset; refresh_time += 1.0f / 60.0f; } @@ -2004,12 +2012,12 @@ static void DemoWindowWidgetsPlotting() // (in this example, we will display an average value) { float average = 0.0f; - for (int n = 0; n < IM_ARRAYSIZE(values); n++) + for (int n = 0; n < IM_COUNTOF(values); n++) average += values[n]; - average /= (float)IM_ARRAYSIZE(values); + average /= (float)IM_COUNTOF(values); char overlay[32]; sprintf(overlay, "avg %f", average); - ImGui::PlotLines("Lines", values, IM_ARRAYSIZE(values), values_offset, overlay, -1.0f, 1.0f, ImVec2(0, 80.0f)); + ImGui::PlotLines("Lines", values, IM_COUNTOF(values), values_offset, overlay, -1.0f, 1.0f, ImVec2(0, 80.0f)); } // Use functions to generate output @@ -2044,10 +2052,12 @@ static void DemoWindowWidgetsProgressBars() if (ImGui::TreeNode("Progress Bars")) { // Animate a simple progress bar - static float progress = 0.0f, progress_dir = 1.0f; - progress += progress_dir * 0.4f * ImGui::GetIO().DeltaTime; - if (progress >= +1.1f) { progress = +1.1f; progress_dir *= -1.0f; } - if (progress <= -0.1f) { progress = -0.1f; progress_dir *= -1.0f; } + static float progress_accum = 0.0f, progress_dir = 1.0f; + progress_accum += progress_dir * 0.4f * ImGui::GetIO().DeltaTime; + if (progress_accum >= +1.1f) { progress_accum = +1.1f; progress_dir *= -1.0f; } + if (progress_accum <= -0.1f) { progress_accum = -0.1f; progress_dir *= -1.0f; } + + const float progress = IM_CLAMP(progress_accum, 0.0f, 1.0f); // Typically we would use ImVec2(-1.0f,0.0f) or ImVec2(-FLT_MIN,0.0f) to use all available width, // or ImVec2(width,0.0f) for a specified width. ImVec2(0.0f,0.0f) uses ItemWidth. @@ -2055,9 +2065,8 @@ static void DemoWindowWidgetsProgressBars() ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::Text("Progress Bar"); - float progress_saturated = IM_CLAMP(progress, 0.0f, 1.0f); char buf[32]; - sprintf(buf, "%d/%d", (int)(progress_saturated * 1753), 1753); + sprintf(buf, "%d/%d", (int)(progress * 1753), 1753); ImGui::ProgressBar(progress, ImVec2(0.f, 0.f), buf); // Pass an animated negative value, e.g. -1.0f * (float)ImGui::GetTime() is the recommended value. @@ -2087,7 +2096,7 @@ static void DemoWindowWidgetsQueryingStatuses() }; static int item_type = 4; static bool item_disabled = false; - ImGui::Combo("Item Type", &item_type, item_names, IM_ARRAYSIZE(item_names), IM_ARRAYSIZE(item_names)); + 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); @@ -2104,8 +2113,8 @@ static void DemoWindowWidgetsQueryingStatuses() if (item_type == 2) { ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true); ret = ImGui::Button("ITEM: Button"); ImGui::PopItemFlag(); } // Testing button (with repeater) if (item_type == 3) { ret = ImGui::Checkbox("ITEM: Checkbox", &b); } // Testing checkbox if (item_type == 4) { ret = ImGui::SliderFloat("ITEM: SliderFloat", &col4f[0], 0.0f, 1.0f); } // Testing basic item - if (item_type == 5) { ret = ImGui::InputText("ITEM: InputText", &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which handles tabbing) - if (item_type == 6) { ret = ImGui::InputTextMultiline("ITEM: InputTextMultiline", &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which uses a child window) + if (item_type == 5) { ret = ImGui::InputText("ITEM: InputText", &str[0], IM_COUNTOF(str)); } // Testing input text (which handles tabbing) + if (item_type == 6) { ret = ImGui::InputTextMultiline("ITEM: InputTextMultiline", &str[0], IM_COUNTOF(str)); } // Testing input text (which uses a child window) if (item_type == 7) { ret = ImGui::InputFloat("ITEM: InputFloat", col4f, 1.0f); } // Testing +/- buttons on scalar input if (item_type == 8) { ret = ImGui::InputFloat3("ITEM: InputFloat3", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) if (item_type == 9) { ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) @@ -2113,8 +2122,8 @@ static void DemoWindowWidgetsQueryingStatuses() if (item_type == 11) { ret = ImGui::MenuItem("ITEM: MenuItem"); } // Testing menu item (they use ImGuiButtonFlags_PressedOnRelease button policy) if (item_type == 12) { ret = ImGui::TreeNode("ITEM: TreeNode"); if (ret) ImGui::TreePop(); } // Testing tree node if (item_type == 13) { ret = ImGui::TreeNodeEx("ITEM: TreeNode w/ ImGuiTreeNodeFlags_OpenOnDoubleClick", ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_NoTreePushOnOpen); } // Testing tree node with ImGuiButtonFlags_PressedOnDoubleClick button policy. - if (item_type == 14) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::Combo("ITEM: Combo", ¤t, items, IM_ARRAYSIZE(items)); } - if (item_type == 15) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); } + if (item_type == 14) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::Combo("ITEM: Combo", ¤t, items, IM_COUNTOF(items)); } + if (item_type == 15) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_COUNTOF(items), IM_COUNTOF(items)); } bool hovered_delay_none = ImGui::IsItemHovered(); bool hovered_delay_stationary = ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary); @@ -2170,7 +2179,7 @@ static void DemoWindowWidgetsQueryingStatuses() ); ImGui::BulletText( "with Hovering Delay or Stationary test:\n" - "IsItemHovered() = = %d\n" + "IsItemHovered() = %d\n" "IsItemHovered(_Stationary) = %d\n" "IsItemHovered(_DelayShort) = %d\n" "IsItemHovered(_DelayNormal) = %d\n" @@ -2181,7 +2190,7 @@ static void DemoWindowWidgetsQueryingStatuses() ImGui::EndDisabled(); char buf[1] = ""; - ImGui::InputText("unused", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_ReadOnly); + ImGui::InputText("unused", buf, IM_COUNTOF(buf), ImGuiInputTextFlags_ReadOnly); ImGui::SameLine(); HelpMarker("This widget is only here to be able to tab-out of the widgets above and see e.g. Deactivated() status."); @@ -2529,7 +2538,7 @@ struct ExampleDualListBox { const int* a = (const int*)lhs; const int* b = (const int*)rhs; - return (*a - *b); + return *a - *b; } void SortItems(int n) { @@ -2647,6 +2656,10 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d { HelpMarker("Selections can be built using Selectable(), TreeNode() or other widgets. Selection state is owned by application code/data."); + ImGui::BulletText("Wiki page:"); + ImGui::SameLine(); + ImGui::TextLinkOpenURL("imgui/wiki/Multi-Select", "https://github.com/ocornut/imgui/wiki/Multi-Select"); + // Without any fancy API: manage single-selection yourself. IMGUI_DEMO_MARKER("Widgets/Selection State/Single-Select"); if (ImGui::TreeNode("Single-Select")) @@ -2663,11 +2676,11 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d } // Demonstrate implementation a most-basic form of multi-selection manually - // This doesn't support the SHIFT modifier which requires BeginMultiSelect()! + // This doesn't support the Shift modifier which requires BeginMultiSelect()! IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (manual/simplified, without BeginMultiSelect)"); if (ImGui::TreeNode("Multi-Select (manual/simplified, without BeginMultiSelect)")) { - HelpMarker("Hold CTRL and click to select multiple items."); + HelpMarker("Hold Ctrl and Click to select multiple items."); static bool selection[5] = { false, false, false, false, false }; for (int n = 0; n < 5; n++) { @@ -2675,7 +2688,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d sprintf(buf, "Object %d", n); if (ImGui::Selectable(buf, selection[n])) { - if (!ImGui::GetIO().KeyCtrl) // Clear selection when CTRL is not held + if (!ImGui::GetIO().KeyCtrl) // Clear selection when Ctrl is not held memset(selection, 0, sizeof(selection)); selection[n] ^= 1; // Toggle current item } @@ -2684,7 +2697,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d } // Demonstrate handling proper multi-selection using the BeginMultiSelect/EndMultiSelect API. - // SHIFT+Click w/ CTRL and other standard features are supported. + // Shift+Click w/ Ctrl and other standard features are supported. // We use the ImGuiSelectionBasicStorage helper which you may freely reimplement. IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select"); if (ImGui::TreeNode("Multi-Select")) @@ -2693,7 +2706,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d ImGui::BulletText("Keyboard navigation (arrows, page up/down, home/end, space)."); ImGui::BulletText("Ctrl modifier to preserve and toggle selection."); ImGui::BulletText("Shift modifier for range selection."); - ImGui::BulletText("CTRL+A to select all."); + ImGui::BulletText("Ctrl+A to select all."); ImGui::BulletText("Escape to clear selection."); ImGui::BulletText("Click and drag to box-select."); ImGui::Text("Tip: Use 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen."); @@ -2713,7 +2726,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d for (int n = 0; n < ITEMS_COUNT; n++) { char label[64]; - sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]); + sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_COUNTOF(ExampleNames)]); bool item_is_selected = selection.Contains((ImGuiID)n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); @@ -2753,7 +2766,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++) { char label[64]; - sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]); + sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_COUNTOF(ExampleNames)]); bool item_is_selected = selection.Contains((ImGuiID)n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); @@ -2815,7 +2828,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d { const ImGuiID item_id = items[n]; char label[64]; - sprintf(label, "Object %05u: %s", item_id, ExampleNames[item_id % IM_ARRAYSIZE(ExampleNames)]); + sprintf(label, "Object %05u: %s", item_id, ExampleNames[item_id % IM_COUNTOF(ExampleNames)]); bool item_is_selected = selection.Contains(item_id); ImGui::SetNextItemSelectionUserData(n); @@ -2841,7 +2854,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d // Init default state static ExampleDualListBox dlb; if (dlb.Items[0].Size == 0 && dlb.Items[1].Size == 0) - for (int item_id = 0; item_id < IM_ARRAYSIZE(ExampleNames); item_id++) + for (int item_id = 0; item_id < IM_COUNTOF(ExampleNames); item_id++) dlb.Items[0].push_back((ImGuiID)item_id); // Show @@ -2858,7 +2871,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d const int ITEMS_COUNT = 10000; ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT); - if (ImGui::BeginTable("##Basket", 2, ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter)) + if (ImGui::BeginTable("##Basket", 2, ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter, ImVec2(0.0f, ImGui::GetFontSize() * 20))) { ImGui::TableSetupColumn("Object"); ImGui::TableSetupColumn("Action"); @@ -2879,13 +2892,15 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::PushID(n); char label[64]; - sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]); + sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_COUNTOF(ExampleNames)]); bool item_is_selected = selection.Contains((ImGuiID)n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap); ImGui::TableNextColumn(); ImGui::SmallButton("hello"); + ImGui::PopID(); } } @@ -2913,7 +2928,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY)) { - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, -1, IM_ARRAYSIZE(items)); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, -1, IM_COUNTOF(items)); ImGuiSelectionExternalStorage storage_wrapper; storage_wrapper.UserData = (void*)items; storage_wrapper.AdapterSetItemSelected = [](ImGuiSelectionExternalStorage* self, int n, bool selected) { bool* array = (bool*)self->UserData; array[n] = selected; }; @@ -2964,7 +2979,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d for (int n = 0; n < ITEMS_COUNT; n++) { char label[64]; - sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]); + sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_COUNTOF(ExampleNames)]); bool item_is_selected = selection->Contains((ImGuiID)n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); @@ -3180,6 +3195,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClearOnReselect", &flags, ImGuiMultiSelectFlags_NoAutoClearOnReselect); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectOnRightClick", &flags, ImGuiMultiSelectFlags_NoSelectOnRightClick); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect1d", &flags, ImGuiMultiSelectFlags_BoxSelect1d); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect2d", &flags, ImGuiMultiSelectFlags_BoxSelect2d); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelectNoScroll", &flags, ImGuiMultiSelectFlags_BoxSelectNoScroll); @@ -3229,7 +3245,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d ImGui::BeginTable("##Split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_NoPadOuterX); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.70f); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.30f); - //ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacingY, 0.0f); + //ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, 0.0f); } ImGuiListClipper clipper; @@ -3252,7 +3268,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d ImGui::TableNextColumn(); const int item_id = items[n]; - const char* item_category = ExampleNames[item_id % IM_ARRAYSIZE(ExampleNames)]; + const char* item_category = ExampleNames[item_id % IM_COUNTOF(ExampleNames)]; char label[64]; sprintf(label, "Object %05d: %s", item_id, item_category); @@ -3314,7 +3330,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d const int* payload_items = (int*)payload->Data; const int payload_count = (int)payload->DataSize / (int)sizeof(int); if (payload_count == 1) - ImGui::Text("Object %05d: %s", payload_items[0], ExampleNames[payload_items[0] % IM_ARRAYSIZE(ExampleNames)]); + ImGui::Text("Object %05d: %s", payload_items[0], ExampleNames[payload_items[0] % IM_COUNTOF(ExampleNames)]); else ImGui::Text("Dragging %d objects", payload_count); @@ -3342,7 +3358,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-FLT_MIN); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGui::InputText("###NoLabel", (char*)(void*)item_category, strlen(item_category), ImGuiInputTextFlags_ReadOnly); + ImGui::InputText("##NoLabel", (char*)(void*)item_category, strlen(item_category), ImGuiInputTextFlags_ReadOnly); ImGui::PopStyleVar(); } @@ -3379,6 +3395,18 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d // [SECTION] DemoWindowWidgetsTabs() //----------------------------------------------------------------------------- +static void EditTabBarFittingPolicyFlags(ImGuiTabBarFlags* p_flags) +{ + if ((*p_flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0) + *p_flags |= ImGuiTabBarFlags_FittingPolicyDefault_; + if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyMixed", p_flags, ImGuiTabBarFlags_FittingPolicyMixed)) + *p_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyMixed); + if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyShrink", p_flags, ImGuiTabBarFlags_FittingPolicyShrink)) + *p_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyShrink); + if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyScroll", p_flags, ImGuiTabBarFlags_FittingPolicyScroll)) + *p_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll); +} + static void DemoWindowWidgetsTabs() { IMGUI_DEMO_MARKER("Widgets/Tabs"); @@ -3421,19 +3449,14 @@ static void DemoWindowWidgetsTabs() ImGui::CheckboxFlags("ImGuiTabBarFlags_TabListPopupButton", &tab_bar_flags, ImGuiTabBarFlags_TabListPopupButton); ImGui::CheckboxFlags("ImGuiTabBarFlags_NoCloseWithMiddleMouseButton", &tab_bar_flags, ImGuiTabBarFlags_NoCloseWithMiddleMouseButton); ImGui::CheckboxFlags("ImGuiTabBarFlags_DrawSelectedOverline", &tab_bar_flags, ImGuiTabBarFlags_DrawSelectedOverline); - if ((tab_bar_flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0) - tab_bar_flags |= ImGuiTabBarFlags_FittingPolicyDefault_; - if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyResizeDown", &tab_bar_flags, ImGuiTabBarFlags_FittingPolicyResizeDown)) - tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyResizeDown); - if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyScroll", &tab_bar_flags, ImGuiTabBarFlags_FittingPolicyScroll)) - tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll); + EditTabBarFittingPolicyFlags(&tab_bar_flags); // Tab Bar ImGui::AlignTextToFramePadding(); ImGui::Text("Opened:"); const char* names[4] = { "Artichoke", "Beetroot", "Celery", "Daikon" }; static bool opened[4] = { true, true, true, true }; // Persistent user state - for (int n = 0; n < IM_ARRAYSIZE(opened); n++) + for (int n = 0; n < IM_COUNTOF(opened); n++) { ImGui::SameLine(); ImGui::Checkbox(names[n], &opened[n]); @@ -3443,7 +3466,7 @@ static void DemoWindowWidgetsTabs() // the underlying bool will be set to false when the tab is closed. if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags)) { - for (int n = 0; n < IM_ARRAYSIZE(opened); n++) + for (int n = 0; n < IM_COUNTOF(opened); n++) if (opened[n] && ImGui::BeginTabItem(names[n], &opened[n], ImGuiTabItemFlags_None)) { ImGui::Text("This is the %s tab!", names[n]); @@ -3475,12 +3498,8 @@ static void DemoWindowWidgetsTabs() ImGui::Checkbox("Show Trailing TabItemButton()", &show_trailing_button); // Expose some other flags which are useful to showcase how they interact with Leading/Trailing tabs - static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_FittingPolicyResizeDown; - ImGui::CheckboxFlags("ImGuiTabBarFlags_TabListPopupButton", &tab_bar_flags, ImGuiTabBarFlags_TabListPopupButton); - if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyResizeDown", &tab_bar_flags, ImGuiTabBarFlags_FittingPolicyResizeDown)) - tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyResizeDown); - if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyScroll", &tab_bar_flags, ImGuiTabBarFlags_FittingPolicyScroll)) - tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll); + static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_FittingPolicyShrink; + EditTabBarFittingPolicyFlags(&tab_bar_flags); if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags)) { @@ -3506,7 +3525,7 @@ static void DemoWindowWidgetsTabs() { bool open = true; char name[16]; - snprintf(name, IM_ARRAYSIZE(name), "%04d", active_tabs[n]); + snprintf(name, IM_COUNTOF(name), "%04d", active_tabs[n]); if (ImGui::BeginTabItem(name, &open, ImGuiTabItemFlags_None)) { ImGui::Text("This is the %s tab!", name); @@ -3548,6 +3567,43 @@ static void DemoWindowWidgetsText() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Text/Font Size"); + if (ImGui::TreeNode("Font Size")) + { + ImGuiStyle& style = ImGui::GetStyle(); + const float global_scale = style.FontScaleMain * style.FontScaleDpi; + ImGui::Text("style.FontScaleMain = %0.2f", style.FontScaleMain); + ImGui::Text("style.FontScaleDpi = %0.2f", style.FontScaleDpi); + ImGui::Text("global_scale = ~%0.2f", global_scale); // This is not technically accurate as internal scales may apply, but conceptually let's pretend it is. + ImGui::Text("FontSize = %0.2f", ImGui::GetFontSize()); + + ImGui::SeparatorText(""); + static float custom_size = 16.0f; + ImGui::SliderFloat("custom_size", &custom_size, 10.0f, 100.0f, "%.0f"); + ImGui::Text("ImGui::PushFont(nullptr, custom_size);"); + ImGui::PushFont(NULL, custom_size); + ImGui::Text("FontSize = %.2f (== %.2f * global_scale)", ImGui::GetFontSize(), custom_size); + ImGui::PopFont(); + + ImGui::SeparatorText(""); + static float custom_scale = 1.0f; + ImGui::SliderFloat("custom_scale", &custom_scale, 0.5f, 4.0f, "%.2f"); + ImGui::Text("ImGui::PushFont(nullptr, style.FontSizeBase * custom_scale);"); + ImGui::PushFont(NULL, style.FontSizeBase * custom_scale); + ImGui::Text("FontSize = %.2f (== style.FontSizeBase * %.2f * global_scale)", ImGui::GetFontSize(), custom_scale); + ImGui::PopFont(); + + ImGui::SeparatorText(""); + for (float scaling = 0.5f; scaling <= 4.0f; scaling += 0.5f) + { + ImGui::PushFont(NULL, style.FontSizeBase * scaling); + ImGui::Text("FontSize = %.2f (== style.FontSizeBase * %.2f * global_scale)", ImGui::GetFontSize(), scaling); + ImGui::PopFont(); + } + + ImGui::TreePop(); + } + IMGUI_DEMO_MARKER("Widgets/Text/Word Wrapping"); if (ImGui::TreeNode("Word Wrapping")) { @@ -3603,7 +3659,7 @@ static void DemoWindowWidgetsText() ImGui::Text("Kanjis: \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e (nihongo)"); static char buf[32] = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"; //static char buf[32] = u8"NIHONGO"; // <- this is how you would write it with C++11, using real kanjis - ImGui::InputText("UTF-8 input", buf, IM_ARRAYSIZE(buf)); + ImGui::InputText("UTF-8 input", buf, IM_COUNTOF(buf)); ImGui::TreePop(); } ImGui::TreePop(); @@ -3630,7 +3686,7 @@ static void DemoWindowWidgetsTextFilter() " \"-xxx\" hide lines containing \"xxx\""); filter.Draw(); const char* lines[] = { "aaa1.c", "bbb1.c", "ccc1.c", "aaa2.cpp", "bbb2.cpp", "ccc2.cpp", "abc.h", "hello, world" }; - for (int i = 0; i < IM_ARRAYSIZE(lines); i++) + for (int i = 0; i < IM_COUNTOF(lines); i++) if (filter.PassFilter(lines[i])) ImGui::BulletText("%s", lines[i]); ImGui::TreePop(); @@ -3651,8 +3707,10 @@ static void DemoWindowWidgetsTextInput() IMGUI_DEMO_MARKER("Widgets/Text Input/Multi-line Text Input"); if (ImGui::TreeNode("Multi-line Text Input")) { - // Note: we are using a fixed-sized buffer for simplicity here. See ImGuiInputTextFlags_CallbackResize - // and the code in misc/cpp/imgui_stdlib.h for how to setup InputText() for dynamically resizing strings. + // WE ARE USING A FIXED-SIZE BUFFER FOR SIMPLICITY HERE. + // If you want to use InputText() with std::string or any custom dynamic string type: + // - For std::string: use the wrapper in misc/cpp/imgui_stdlib.h/.cpp + // - Otherwise, see the 'Dear ImGui Demo->Widgets->Text Input->Resize Callback' for using ImGuiInputTextFlags_CallbackResize. static char text[1024 * 16] = "/*\n" " The Pentium F00F bug, shorthand for F0 0F C7 C8,\n" @@ -3668,10 +3726,12 @@ static void DemoWindowWidgetsTextInput() static ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput; HelpMarker("You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputTextMultiline() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example. (This is not demonstrated in imgui_demo.cpp because we don't want to include in here)"); ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly); + ImGui::CheckboxFlags("ImGuiInputTextFlags_WordWrap", &flags, ImGuiInputTextFlags_WordWrap); + ImGui::SameLine(); HelpMarker("Feature is currently in Beta. Please read comments in imgui.h"); ImGui::CheckboxFlags("ImGuiInputTextFlags_AllowTabInput", &flags, ImGuiInputTextFlags_AllowTabInput); ImGui::SameLine(); HelpMarker("When _AllowTabInput is set, passing through the widget with Tabbing doesn't automatically activate it, in order to also cycling through subsequent widgets."); ImGui::CheckboxFlags("ImGuiInputTextFlags_CtrlEnterForNewLine", &flags, ImGuiInputTextFlags_CtrlEnterForNewLine); - ImGui::InputTextMultiline("##source", text, IM_ARRAYSIZE(text), ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags); + ImGui::InputTextMultiline("##source", text, IM_COUNTOF(text), ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags); ImGui::TreePop(); } @@ -3697,13 +3757,13 @@ static void DemoWindowWidgetsTextInput() } }; - static char buf1[32] = ""; ImGui::InputText("default", buf1, IM_ARRAYSIZE(buf1)); - static char buf2[32] = ""; ImGui::InputText("decimal", buf2, IM_ARRAYSIZE(buf2), ImGuiInputTextFlags_CharsDecimal); - static char buf3[32] = ""; ImGui::InputText("hexadecimal", buf3, IM_ARRAYSIZE(buf3), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); - static char buf4[32] = ""; ImGui::InputText("uppercase", buf4, IM_ARRAYSIZE(buf4), ImGuiInputTextFlags_CharsUppercase); - static char buf5[32] = ""; ImGui::InputText("no blank", buf5, IM_ARRAYSIZE(buf5), ImGuiInputTextFlags_CharsNoBlank); - static char buf6[32] = ""; ImGui::InputText("casing swap", buf6, IM_ARRAYSIZE(buf6), ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterCasingSwap); // Use CharFilter callback to replace characters. - static char buf7[32] = ""; ImGui::InputText("\"imgui\"", buf7, IM_ARRAYSIZE(buf7), ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); // Use CharFilter callback to disable some characters. + static char buf1[32] = ""; ImGui::InputText("default", buf1, IM_COUNTOF(buf1)); + static char buf2[32] = ""; ImGui::InputText("decimal", buf2, IM_COUNTOF(buf2), ImGuiInputTextFlags_CharsDecimal); + static char buf3[32] = ""; ImGui::InputText("hexadecimal", buf3, IM_COUNTOF(buf3), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); + static char buf4[32] = ""; ImGui::InputText("uppercase", buf4, IM_COUNTOF(buf4), ImGuiInputTextFlags_CharsUppercase); + static char buf5[32] = ""; ImGui::InputText("no blank", buf5, IM_COUNTOF(buf5), ImGuiInputTextFlags_CharsNoBlank); + static char buf6[32] = ""; ImGui::InputText("casing swap", buf6, IM_COUNTOF(buf6), ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterCasingSwap); // Use CharFilter callback to replace characters. + static char buf7[32] = ""; ImGui::InputText("\"imgui\"", buf7, IM_COUNTOF(buf7), ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); // Use CharFilter callback to disable some characters. ImGui::TreePop(); } @@ -3711,10 +3771,10 @@ static void DemoWindowWidgetsTextInput() if (ImGui::TreeNode("Password Input")) { static char password[64] = "password123"; - ImGui::InputText("password", password, IM_ARRAYSIZE(password), ImGuiInputTextFlags_Password); + ImGui::InputText("password", password, IM_COUNTOF(password), ImGuiInputTextFlags_Password); ImGui::SameLine(); HelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n"); - ImGui::InputTextWithHint("password (w/ hint)", "", password, IM_ARRAYSIZE(password), ImGuiInputTextFlags_Password); - ImGui::InputText("password (clear)", password, IM_ARRAYSIZE(password)); + ImGui::InputTextWithHint("password (w/ hint)", "", password, IM_COUNTOF(password), ImGuiInputTextFlags_Password); + ImGui::InputText("password (clear)", password, IM_COUNTOF(password)); ImGui::TreePop(); } @@ -3759,20 +3819,20 @@ static void DemoWindowWidgetsTextInput() } }; static char buf1[64]; - ImGui::InputText("Completion", buf1, IM_ARRAYSIZE(buf1), ImGuiInputTextFlags_CallbackCompletion, Funcs::MyCallback); + ImGui::InputText("Completion", buf1, IM_COUNTOF(buf1), ImGuiInputTextFlags_CallbackCompletion, Funcs::MyCallback); ImGui::SameLine(); HelpMarker( "Here we append \"..\" each time Tab is pressed. " "See 'Examples>Console' for a more meaningful demonstration of using this callback."); static char buf2[64]; - ImGui::InputText("History", buf2, IM_ARRAYSIZE(buf2), ImGuiInputTextFlags_CallbackHistory, Funcs::MyCallback); + ImGui::InputText("History", buf2, IM_COUNTOF(buf2), ImGuiInputTextFlags_CallbackHistory, Funcs::MyCallback); ImGui::SameLine(); HelpMarker( "Here we replace and select text each time Up/Down are pressed. " "See 'Examples>Console' for a more meaningful demonstration of using this callback."); static char buf3[64]; static int edit_count = 0; - ImGui::InputText("Edit", buf3, IM_ARRAYSIZE(buf3), ImGuiInputTextFlags_CallbackEdit, Funcs::MyCallback, (void*)&edit_count); + ImGui::InputText("Edit", buf3, IM_COUNTOF(buf3), ImGuiInputTextFlags_CallbackEdit, Funcs::MyCallback, (void*)&edit_count); ImGui::SameLine(); HelpMarker( "Here we toggle the casing of the first character on every edit + count edits."); ImGui::SameLine(); ImGui::Text("(%d)", edit_count); @@ -3815,10 +3875,13 @@ static void DemoWindowWidgetsTextInput() // For this demo we are using ImVector as a string container. // Note that because we need to store a terminating zero character, our size/capacity are 1 more // than usually reported by a typical string class. + static ImGuiInputTextFlags flags = ImGuiInputTextFlags_None; + ImGui::CheckboxFlags("ImGuiInputTextFlags_WordWrap", &flags, ImGuiInputTextFlags_WordWrap); + static ImVector my_str; if (my_str.empty()) my_str.push_back(0); - Funcs::MyInputTextMultiline("##MyStr", &my_str, ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16)); + Funcs::MyInputTextMultiline("##MyStr", &my_str, ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags); ImGui::Text("Data: %p\nSize: %d\nCapacity: %d", (void*)my_str.begin(), my_str.size(), my_str.capacity()); ImGui::TreePop(); } @@ -3829,7 +3892,7 @@ static void DemoWindowWidgetsTextInput() static char buf1[128] = "/path/to/some/folder/with/long/filename.cpp"; static ImGuiInputTextFlags flags = ImGuiInputTextFlags_ElideLeft; ImGui::CheckboxFlags("ImGuiInputTextFlags_ElideLeft", &flags, ImGuiInputTextFlags_ElideLeft); - ImGui::InputText("Path", buf1, IM_ARRAYSIZE(buf1), flags); + ImGui::InputText("Path", buf1, IM_COUNTOF(buf1), flags); ImGui::TreePop(); } @@ -3841,7 +3904,7 @@ static void DemoWindowWidgetsTextInput() ImGui::CheckboxFlags("ImGuiInputTextFlags_EscapeClearsAll", &flags, ImGuiInputTextFlags_EscapeClearsAll); ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly); ImGui::CheckboxFlags("ImGuiInputTextFlags_NoUndoRedo", &flags, ImGuiInputTextFlags_NoUndoRedo); - ImGui::InputText("Hello", buf1, IM_ARRAYSIZE(buf1), flags); + ImGui::InputText("Hello", buf1, IM_COUNTOF(buf1), flags); ImGui::TreePop(); } @@ -3883,7 +3946,7 @@ static void DemoWindowWidgetsTooltips() { ImGui::Text("I am a fancy tooltip"); static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; - ImGui::PlotLines("Curve", arr, IM_ARRAYSIZE(arr)); + ImGui::PlotLines("Curve", arr, IM_COUNTOF(arr)); ImGui::Text("Sin(time) = %f", sinf((float)ImGui::GetTime())); ImGui::EndTooltip(); } @@ -4019,7 +4082,7 @@ static void DemoWindowWidgetsTreeNodes() { 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."); + "Click to select, Ctrl+Click to toggle, click on arrows or double-click to open."); 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; @@ -4031,6 +4094,7 @@ static void DemoWindowWidgetsTreeNodes() ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAllColumns", &base_flags, ImGuiTreeNodeFlags_SpanAllColumns); ImGui::SameLine(); HelpMarker("For use in Tables only."); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_AllowOverlap", &base_flags, ImGuiTreeNodeFlags_AllowOverlap); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_Framed", &base_flags, ImGuiTreeNodeFlags_Framed); ImGui::SameLine(); HelpMarker("Draw frame with background (e.g. for CollapsingHeader)"); + ImGui::CheckboxFlags("ImGuiTreeNodeFlags_FramePadding", &base_flags, ImGuiTreeNodeFlags_FramePadding); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_NavLeftJumpsToParent", &base_flags, ImGuiTreeNodeFlags_NavLeftJumpsToParent); HelpMarker("Default option for DrawLinesXXX is stored in style.TreeLinesFlags"); @@ -4106,7 +4170,7 @@ static void DemoWindowWidgetsTreeNodes() // 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 + 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 } @@ -4518,23 +4582,21 @@ static void DemoWindowLayout() // Various static float f0 = 1.0f, f1 = 2.0f, f2 = 3.0f; - ImGui::PushItemWidth(80); + ImGui::PushItemWidth(ImGui::CalcTextSize("AAAAAAA").x); const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD" }; static int item = -1; - ImGui::Combo("Combo", &item, items, IM_ARRAYSIZE(items)); ImGui::SameLine(); + ImGui::Combo("Combo", &item, items, IM_COUNTOF(items)); ImGui::SameLine(); ImGui::SliderFloat("X", &f0, 0.0f, 5.0f); ImGui::SameLine(); ImGui::SliderFloat("Y", &f1, 0.0f, 5.0f); ImGui::SameLine(); ImGui::SliderFloat("Z", &f2, 0.0f, 5.0f); - ImGui::PopItemWidth(); - ImGui::PushItemWidth(80); ImGui::Text("Lists:"); static int selection[4] = { 0, 1, 2, 3 }; for (int i = 0; i < 4; i++) { if (i > 0) ImGui::SameLine(); ImGui::PushID(i); - ImGui::ListBox("", &selection[i], items, IM_ARRAYSIZE(items)); + ImGui::ListBox("", &selection[i], items, IM_COUNTOF(items)); ImGui::PopID(); //ImGui::SetItemTooltip("ListBox %d hovered", i); } @@ -4594,7 +4656,7 @@ static void DemoWindowLayout() // Capture the group size and create widgets using the same size ImVec2 size = ImGui::GetItemRectSize(); const float values[5] = { 0.5f, 0.20f, 0.80f, 0.60f, 0.25f }; - ImGui::PlotHistogram("##values", values, IM_ARRAYSIZE(values), 0, NULL, 0.0f, 1.0f, size); + ImGui::PlotHistogram("##values", values, IM_COUNTOF(values), 0, NULL, 0.0f, 1.0f, size); ImGui::Button("ACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x) * 0.5f, size.y)); ImGui::SameLine(); @@ -4695,7 +4757,7 @@ static void DemoWindowLayout() // Tree // (here the node appears after a button and has odd intent, so we use ImGuiTreeNodeFlags_DrawLinesNone to disable hierarchy outline) const float spacing = ImGui::GetStyle().ItemInnerSpacing.x; - ImGui::Button("Button##1"); + ImGui::Button("Button##1"); // Will make line higher ImGui::SameLine(0.0f, spacing); if (ImGui::TreeNodeEx("Node##1", ImGuiTreeNodeFlags_DrawLinesNone)) { @@ -4705,14 +4767,22 @@ static void DemoWindowLayout() ImGui::TreePop(); } + const float padding = (float)(int)(ImGui::GetFontSize() * 1.20f); // Large padding + ImGui::PushStyleVarY(ImGuiStyleVar_FramePadding, padding); + ImGui::Button("Button##2"); + ImGui::PopStyleVar(); + ImGui::SameLine(0.0f, spacing); + if (ImGui::TreeNodeEx("Node##2", ImGuiTreeNodeFlags_DrawLinesNone)) + ImGui::TreePop(); + // Vertically align text node a bit lower so it'll be vertically centered with upcoming widget. // Otherwise you can use SmallButton() (smaller fit). ImGui::AlignTextToFramePadding(); // Common mistake to avoid: if we want to SameLine after TreeNode we need to do it before we add - // other contents below the node. - bool node_open = ImGui::TreeNode("Node##2"); - ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##2"); + // other contents "inside" the node. + bool node_open = ImGui::TreeNode("Node##3"); + ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##3"); if (node_open) { // Placeholder tree data @@ -4722,13 +4792,13 @@ static void DemoWindowLayout() } // Bullet - ImGui::Button("Button##3"); + ImGui::Button("Button##4"); ImGui::SameLine(0.0f, spacing); ImGui::BulletText("Bullet text"); ImGui::AlignTextToFramePadding(); ImGui::BulletText("Node"); - ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##4"); + ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##5"); ImGui::Unindent(); } @@ -4750,15 +4820,18 @@ static void DemoWindowLayout() ImGui::Checkbox("Decoration", &enable_extra_decorations); + ImGui::PushItemWidth(ImGui::GetFontSize() * 10); + enable_track |= ImGui::DragInt("##item", &track_item, 0.25f, 0, 99, "Item = %d"); + ImGui::SameLine(); ImGui::Checkbox("Track", &enable_track); - ImGui::PushItemWidth(100); - ImGui::SameLine(140); enable_track |= ImGui::DragInt("##item", &track_item, 0.25f, 0, 99, "Item = %d"); - bool scroll_to_off = ImGui::Button("Scroll Offset"); - ImGui::SameLine(140); scroll_to_off |= ImGui::DragFloat("##off", &scroll_to_off_px, 1.00f, 0, FLT_MAX, "+%.0f px"); + bool scroll_to_off = ImGui::DragFloat("##off", &scroll_to_off_px, 1.00f, 0, FLT_MAX, "+%.0f px"); + ImGui::SameLine(); + scroll_to_off |= ImGui::Button("Scroll Offset"); - bool scroll_to_pos = ImGui::Button("Scroll To Pos"); - ImGui::SameLine(140); scroll_to_pos |= ImGui::DragFloat("##pos", &scroll_to_pos_px, 1.00f, -10, FLT_MAX, "X/Y = %.0f px"); + bool scroll_to_pos = ImGui::DragFloat("##pos", &scroll_to_pos_px, 1.00f, -10, FLT_MAX, "X/Y = %.0f px"); + ImGui::SameLine(); + scroll_to_pos |= ImGui::Button("Scroll To Pos"); ImGui::PopItemWidth(); if (scroll_to_off || scroll_to_pos) @@ -4951,7 +5024,7 @@ static void DemoWindowLayout() if (explicit_content_size) { ImGui::SameLine(); - ImGui::SetNextItemWidth(100); + ImGui::SetNextItemWidth(ImGui::CalcTextSize("123456").x); ImGui::DragFloat("##csx", &contents_size_x); ImVec2 p = ImGui::GetCursorScreenPos(); ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + 10, p.y + 10), IM_COL32_WHITE); @@ -5142,7 +5215,7 @@ static void DemoWindowPopups() // Typical use for regular windows: // bool my_tool_is_active = false; if (ImGui::Button("Open")) my_tool_is_active = true; [...] if (my_tool_is_active) Begin("My Tool", &my_tool_is_active) { [...] } End(); // Typical use for popups: - // if (ImGui::Button("Open")) ImGui::OpenPopup("MyPopup"); if (ImGui::BeginPopup("MyPopup") { [...] EndPopup(); } + // if (ImGui::Button("Open")) ImGui::OpenPopup("MyPopup"); if (ImGui::BeginPopup("MyPopup")) { [...] EndPopup(); } // With popups we have to go through a library call (here OpenPopup) to manipulate the visibility state. // This may be a bit confusing at first but it should quickly make sense. Follow on the examples below. @@ -5167,7 +5240,7 @@ static void DemoWindowPopups() if (ImGui::BeginPopup("my_select_popup")) { ImGui::SeparatorText("Aquarium"); - for (int i = 0; i < IM_ARRAYSIZE(names); i++) + for (int i = 0; i < IM_COUNTOF(names); i++) if (ImGui::Selectable(names[i])) selected_fish = i; ImGui::EndPopup(); @@ -5178,7 +5251,7 @@ static void DemoWindowPopups() ImGui::OpenPopup("my_toggle_popup"); if (ImGui::BeginPopup("my_toggle_popup")) { - for (int i = 0; i < IM_ARRAYSIZE(names); i++) + for (int i = 0; i < IM_COUNTOF(names); i++) ImGui::MenuItem(names[i], "", &toggles[i]); if (ImGui::BeginMenu("Sub-menu")) { @@ -5194,7 +5267,7 @@ static void DemoWindowPopups() ImGui::OpenPopup("another popup"); if (ImGui::BeginPopup("another popup")) { - for (int i = 0; i < IM_ARRAYSIZE(names); i++) + for (int i = 0; i < IM_COUNTOF(names); i++) ImGui::MenuItem(names[i], "", &toggles[i]); if (ImGui::BeginMenu("Sub-menu")) { @@ -5267,7 +5340,7 @@ static void DemoWindowPopups() if (ImGui::BeginPopupContextItem()) // <-- use last item id as popup id { selected = n; - ImGui::Text("This a popup for \"%s\"!", names[n]); + ImGui::Text("This is a popup for \"%s\"!", names[n]); if (ImGui::Button("Close")) ImGui::CloseCurrentPopup(); ImGui::EndPopup(); @@ -5316,7 +5389,7 @@ static void DemoWindowPopups() if (ImGui::BeginPopupContextItem()) { ImGui::Text("Edit name:"); - ImGui::InputText("##edit", name, IM_ARRAYSIZE(name)); + ImGui::InputText("##edit", name, IM_COUNTOF(name)); if (ImGui::Button("Close")) ImGui::CloseCurrentPopup(); ImGui::EndPopup(); @@ -5410,7 +5483,7 @@ static void DemoWindowPopups() ImGui::TextWrapped("Below we are testing adding menu items to a regular window. It's rather unusual but should work!"); ImGui::Separator(); - ImGui::MenuItem("Menu item", "CTRL+M"); + ImGui::MenuItem("Menu item", "Ctrl+M"); if (ImGui::BeginMenu("Menu inside a regular window")) { ShowExampleMenuFile(); @@ -5489,7 +5562,7 @@ struct MyItem // qsort() is instable so always return a way to differentiate items. // Your own compare function may want to avoid fallback on implicit sort specs. // e.g. a Name compare if it wasn't already part of the sort specs. - return (a->ID - b->ID); + return a->ID - b->ID; } }; const ImGuiTableSortSpecs* MyItem::s_current_sort_specs = NULL; @@ -5521,13 +5594,13 @@ static void EditTableSizingFlags(ImGuiTableFlags* p_flags) { ImGuiTableFlags_SizingStretchSame, "ImGuiTableFlags_SizingStretchSame", "Columns default to _WidthStretch with same weights." } }; int idx; - for (idx = 0; idx < IM_ARRAYSIZE(policies); idx++) + for (idx = 0; idx < IM_COUNTOF(policies); idx++) if (policies[idx].Value == (*p_flags & ImGuiTableFlags_SizingMask_)) break; - const char* preview_text = (idx < IM_ARRAYSIZE(policies)) ? policies[idx].Name + (idx > 0 ? strlen("ImGuiTableFlags") : 0) : ""; + const char* preview_text = (idx < IM_COUNTOF(policies)) ? policies[idx].Name + (idx > 0 ? strlen("ImGuiTableFlags") : 0) : ""; if (ImGui::BeginCombo("Sizing Policy", preview_text)) { - for (int n = 0; n < IM_ARRAYSIZE(policies); n++) + for (int n = 0; n < IM_COUNTOF(policies); n++) if (ImGui::Selectable(policies[n].Name, idx == n)) *p_flags = (*p_flags & ~ImGuiTableFlags_SizingMask_) | policies[n].Value; ImGui::EndCombo(); @@ -5537,7 +5610,7 @@ static void EditTableSizingFlags(ImGuiTableFlags* p_flags) if (ImGui::BeginItemTooltip()) { ImGui::PushTextWrapPos(ImGui::GetFontSize() * 50.0f); - for (int m = 0; m < IM_ARRAYSIZE(policies); m++) + for (int m = 0; m < IM_COUNTOF(policies); m++) { ImGui::Separator(); ImGui::Text("%s:", policies[m].Name); @@ -5728,7 +5801,7 @@ static void DemoWindowTables() ImGui::SameLine(); ImGui::RadioButton("Text", &contents_type, CT_Text); ImGui::SameLine(); ImGui::RadioButton("FillButton", &contents_type, CT_FillButton); ImGui::Checkbox("Display headers", &display_headers); - ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers"); + ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers)"); PopStyleCompact(); if (ImGui::BeginTable("table1", 3, flags)) @@ -6035,7 +6108,7 @@ static void DemoWindowTables() strcpy(text_bufs[cell], "edit me"); ImGui::SetNextItemWidth(-FLT_MIN); ImGui::PushID(cell); - ImGui::InputText("##cell", text_bufs[cell], IM_ARRAYSIZE(text_bufs[cell])); + ImGui::InputText("##cell", text_bufs[cell], IM_COUNTOF(text_bufs[cell])); ImGui::PopID(); } if (!show_widget_frame_bg) @@ -6148,7 +6221,7 @@ static void DemoWindowTables() case CT_ShowWidth: ImGui::Text("W: %.1f", ImGui::GetContentRegionAvail().x); break; case CT_Button: ImGui::Button(label); break; case CT_FillButton: ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f)); break; - case CT_InputText: ImGui::SetNextItemWidth(-FLT_MIN); ImGui::InputText("##", text_buf, IM_ARRAYSIZE(text_buf)); break; + case CT_InputText: ImGui::SetNextItemWidth(-FLT_MIN); ImGui::InputText("##", text_buf, IM_COUNTOF(text_buf)); break; } ImGui::PopID(); } @@ -6453,7 +6526,7 @@ static void DemoWindowTables() ImGui::TableNextColumn(); ImGui::Text("A0 Row 0"); { - float rows_height = TEXT_BASE_HEIGHT * 2; + float rows_height = (TEXT_BASE_HEIGHT * 2.0f) + (ImGui::GetStyle().CellPadding.y * 2.0f); if (ImGui::BeginTable("table_nested2", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable)) { ImGui::TableSetupColumn("B0"); @@ -6495,7 +6568,7 @@ static void DemoWindowTables() { for (int row = 0; row < 8; row++) { - float min_row_height = (float)(int)(TEXT_BASE_HEIGHT * 0.30f * row); + float min_row_height = (float)(int)(TEXT_BASE_HEIGHT * 0.30f * row + ImGui::GetStyle().CellPadding.y * 2.0f); ImGui::TableNextRow(ImGuiTableRowFlags_None, min_row_height); ImGui::TableNextColumn(); ImGui::Text("min_row_height = %.2f", min_row_height); @@ -6599,9 +6672,10 @@ static void DemoWindowTables() ImGui::SameLine(); if (ImGui::BeginTable("table3", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(TEXT_BASE_WIDTH * 30, 0.0f))) { + const float rows_height = TEXT_BASE_HEIGHT * 1.5f + ImGui::GetStyle().CellPadding.y * 2.0f; for (int row = 0; row < 3; row++) { - ImGui::TableNextRow(0, TEXT_BASE_HEIGHT * 1.5f); + ImGui::TableNextRow(0, rows_height); for (int column = 0; column < 3; column++) { ImGui::TableNextColumn(); @@ -6866,7 +6940,7 @@ static void DemoWindowTables() if (ImGui::TreeNode("Angled headers")) { const char* column_names[] = { "Track", "cabasa", "ride", "smash", "tom-hi", "tom-mid", "tom-low", "hihat-o", "hihat-c", "snare-s", "snare-c", "clap", "rim", "kick" }; - const int columns_count = IM_ARRAYSIZE(column_names); + const int columns_count = IM_COUNTOF(column_names); const int rows_count = 12; static ImGuiTableFlags table_flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_Hideable | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_HighlightHoveredColumn; @@ -6975,7 +7049,7 @@ static void DemoWindowTables() // [2.3] Right-click in columns to open another custom popup HelpMarker( "Demonstrate mixing table context menu (over header), item context button (over button) " - "and custom per-colunm context menu (over column body)."); + "and custom per-column context menu (over column body)."); ImGuiTableFlags flags2 = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders; if (ImGui::BeginTable("table_context_menu_2", COLUMNS_COUNT, flags2)) { @@ -7095,7 +7169,7 @@ static void DemoWindowTables() items.resize(50, MyItem()); for (int n = 0; n < items.Size; n++) { - const int template_n = n % IM_ARRAYSIZE(template_items_names); + const int template_n = n % IM_COUNTOF(template_items_names); MyItem& item = items[n]; item.ID = n; item.Name = template_items_names[template_n]; @@ -7186,7 +7260,7 @@ static void DemoWindowTables() const char* contents_type_names[] = { "Text", "Button", "SmallButton", "FillButton", "Selectable", "Selectable (span row)" }; static int freeze_cols = 1; static int freeze_rows = 1; - static int items_count = IM_ARRAYSIZE(template_items_names) * 2; + static int items_count = IM_COUNTOF(template_items_names) * 2; static ImVec2 outer_size_value = ImVec2(0.0f, TEXT_BASE_HEIGHT * 12); static float row_min_height = 0.0f; // Auto static float inner_width_with_scroll = 0.0f; // Auto-extend @@ -7221,7 +7295,7 @@ static void DemoWindowTables() ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags, ImGuiTableFlags_BordersH); ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterH", &flags, ImGuiTableFlags_BordersOuterH); ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerH", &flags, ImGuiTableFlags_BordersInnerH); - ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers"); + ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers)"); ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags, ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)"); ImGui::TreePop(); } @@ -7304,7 +7378,7 @@ static void DemoWindowTables() ImGui::SameLine(); HelpMarker("Specify height of the Selectable item."); ImGui::DragInt("items_count", &items_count, 0.1f, 0, 9999); - ImGui::Combo("items_type (first column)", &contents_type, contents_type_names, IM_ARRAYSIZE(contents_type_names)); + ImGui::Combo("items_type (first column)", &contents_type, contents_type_names, IM_COUNTOF(contents_type_names)); //filter.Draw("filter"); ImGui::TreePop(); } @@ -7324,7 +7398,7 @@ static void DemoWindowTables() items.resize(items_count, MyItem()); for (int n = 0; n < items_count; n++) { - const int template_n = n % IM_ARRAYSIZE(template_items_names); + const int template_n = n % IM_COUNTOF(template_items_names); MyItem& item = items[n]; item.ID = n; item.Name = template_items_names[template_n]; @@ -7726,10 +7800,10 @@ static void DemoWindowInputs() ImGui::Text("Mouse pos: "); ImGui::Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y); ImGui::Text("Mouse down:"); - for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } + for (int i = 0; i < IM_COUNTOF(io.MouseDown); i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } ImGui::Text("Mouse wheel: %.1f", io.MouseWheel); ImGui::Text("Mouse clicked count:"); - for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (io.MouseClickedCount[i] > 0) { ImGui::SameLine(); ImGui::Text("b%d: %d", i, io.MouseClickedCount[i]); } + for (int i = 0; i < IM_COUNTOF(io.MouseDown); i++) if (io.MouseClickedCount[i] > 0) { ImGui::SameLine(); ImGui::Text("b%d: %d", i, io.MouseClickedCount[i]); } // We iterate both legacy native range and named ImGuiKey ranges. This is a little unusual/odd but this allows // displaying the data for old/new backends. @@ -7841,18 +7915,18 @@ static void DemoWindowInputs() ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 0.0f, 1.0f, 0.1f)); ImGui::BeginChild("WindowA", ImVec2(-FLT_MIN, line_height * 14), true); - ImGui::Text("Press CTRL+A and see who receives it!"); + ImGui::Text("Press Ctrl+A and see who receives it!"); ImGui::Separator(); - // 1: Window polling for CTRL+A + // 1: Window polling for Ctrl+A ImGui::Text("(in WindowA)"); ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "..."); - // 2: InputText also polling for CTRL+A: it always uses _RouteFocused internally (gets priority when active) + // 2: InputText also polling for Ctrl+A: it always uses _RouteFocused internally (gets priority when active) // (Commented because the owner-aware version of Shortcut() is still in imgui_internal.h) - //char str[16] = "Press CTRL+A"; + //char str[16] = "Press Ctrl+A"; //ImGui::Spacing(); - //ImGui::InputText("InputTextB", str, IM_ARRAYSIZE(str), ImGuiInputTextFlags_ReadOnly); + //ImGui::InputText("InputTextB", str, IM_COUNTOF(str), ImGuiInputTextFlags_ReadOnly); //ImGuiID item_id = ImGui::GetItemID(); //ImGui::SameLine(); HelpMarker("Internal widgets always use _RouteFocused"); //ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags, item_id) ? "PRESSED" : "..."); @@ -7863,7 +7937,7 @@ static void DemoWindowInputs() ImGui::Text("IsWindowFocused: %d", ImGui::IsWindowFocused()); ImGui::EndChild(); - // 4: Child window polling for CTRL+A. It is deeper than WindowA and gets priority when focused. + // 4: Child window polling for Ctrl+A. It is deeper than WindowA and gets priority when focused. ImGui::BeginChild("ChildE", ImVec2(-FLT_MIN, line_height * 4), true); ImGui::Text("(in ChildE: using same Shortcut)"); ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "..."); @@ -7877,7 +7951,7 @@ static void DemoWindowInputs() ImGui::Text("(in PopupF)"); ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "..."); // (Commented because the owner-aware version of Shortcut() is still in imgui_internal.h) - //ImGui::InputText("InputTextG", str, IM_ARRAYSIZE(str), ImGuiInputTextFlags_ReadOnly); + //ImGui::InputText("InputTextG", str, IM_COUNTOF(str), ImGuiInputTextFlags_ReadOnly); //ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags, ImGui::GetItemID()) ? "PRESSED" : "..."); ImGui::EndPopup(); } @@ -7892,7 +7966,7 @@ static void DemoWindowInputs() if (ImGui::TreeNode("Mouse Cursors")) { const char* mouse_cursors_names[] = { "Arrow", "TextInput", "ResizeAll", "ResizeNS", "ResizeEW", "ResizeNESW", "ResizeNWSE", "Hand", "Wait", "Progress", "NotAllowed" }; - IM_ASSERT(IM_ARRAYSIZE(mouse_cursors_names) == ImGuiMouseCursor_COUNT); + IM_ASSERT(IM_COUNTOF(mouse_cursors_names) == ImGuiMouseCursor_COUNT); ImGuiMouseCursor current = ImGui::GetMouseCursor(); const char* cursor_name = (current >= ImGuiMouseCursor_Arrow) && (current < ImGuiMouseCursor_COUNT) ? mouse_cursors_names[current] : "N/A"; @@ -7920,16 +7994,16 @@ static void DemoWindowInputs() IMGUI_DEMO_MARKER("Inputs & Focus/Tabbing"); if (ImGui::TreeNode("Tabbing")) { - ImGui::Text("Use TAB/SHIFT+TAB to cycle through keyboard editable fields."); + ImGui::Text("Use Tab/Shift+Tab to cycle through keyboard editable fields."); static char buf[32] = "hello"; - ImGui::InputText("1", buf, IM_ARRAYSIZE(buf)); - ImGui::InputText("2", buf, IM_ARRAYSIZE(buf)); - ImGui::InputText("3", buf, IM_ARRAYSIZE(buf)); + ImGui::InputText("1", buf, IM_COUNTOF(buf)); + ImGui::InputText("2", buf, IM_COUNTOF(buf)); + ImGui::InputText("3", buf, IM_COUNTOF(buf)); ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, true); - ImGui::InputText("4 (tab skip)", buf, IM_ARRAYSIZE(buf)); + ImGui::InputText("4 (tab skip)", buf, IM_COUNTOF(buf)); ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab."); ImGui::PopItemFlag(); - ImGui::InputText("5", buf, IM_ARRAYSIZE(buf)); + ImGui::InputText("5", buf, IM_COUNTOF(buf)); ImGui::TreePop(); } @@ -7943,16 +8017,16 @@ static void DemoWindowInputs() static char buf[128] = "click on a button to set focus"; if (focus_1) ImGui::SetKeyboardFocusHere(); - ImGui::InputText("1", buf, IM_ARRAYSIZE(buf)); + ImGui::InputText("1", buf, IM_COUNTOF(buf)); if (ImGui::IsItemActive()) has_focus = 1; if (focus_2) ImGui::SetKeyboardFocusHere(); - ImGui::InputText("2", buf, IM_ARRAYSIZE(buf)); + ImGui::InputText("2", buf, IM_COUNTOF(buf)); if (ImGui::IsItemActive()) has_focus = 2; ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, true); if (focus_3) ImGui::SetKeyboardFocusHere(); - ImGui::InputText("3 (tab skip)", buf, IM_ARRAYSIZE(buf)); + ImGui::InputText("3 (tab skip)", buf, IM_COUNTOF(buf)); if (ImGui::IsItemActive()) has_focus = 3; ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab."); ImGui::PopItemFlag(); @@ -8034,7 +8108,7 @@ void ImGui::ShowAboutWindow(bool* p_open) ImGui::TextLinkOpenURL("Funding", "https://github.com/ocornut/imgui/wiki/Funding"); ImGui::Separator(); - ImGui::Text("(c) 2014-2025 Omar Cornut"); + ImGui::Text("(c) 2014-2026 Omar Cornut"); ImGui::Text("Developed by Omar Cornut and all Dear ImGui contributors."); ImGui::Text("Dear ImGui is licensed under the MIT License, see LICENSE for more information."); ImGui::Text("If your company uses this, please consider funding the project."); @@ -8059,6 +8133,9 @@ void ImGui::ShowAboutWindow(bool* p_open) ImGui::Separator(); ImGui::Text("sizeof(size_t): %d, sizeof(ImDrawIdx): %d, sizeof(ImDrawVert): %d", (int)sizeof(size_t), (int)sizeof(ImDrawIdx), (int)sizeof(ImDrawVert)); ImGui::Text("define: __cplusplus=%d", (int)__cplusplus); +#ifdef IMGUI_ENABLE_TEST_ENGINE + ImGui::Text("define: IMGUI_ENABLE_TEST_ENGINE"); +#endif #ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImGui::Text("define: IMGUI_DISABLE_OBSOLETE_FUNCTIONS"); #endif @@ -8126,6 +8203,25 @@ void ImGui::ShowAboutWindow(bool* p_open) ImGui::Text("define: __EMSCRIPTEN__"); ImGui::Text("Emscripten: %d.%d.%d", __EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__); #endif +#ifdef NDEBUG + ImGui::Text("define: NDEBUG"); +#endif + + // Heuristic to detect no-op IM_ASSERT() macros + // - This is designed so people opening bug reports would convey and notice that they have disabled asserts for Dear ImGui code. + // - 16 is > strlen("((void)(_EXPR))") which we suggested in our imconfig.h template as a possible way to disable. + int assert_runs_expression = 0; + IM_ASSERT(++assert_runs_expression); + int assert_expand_len = (int)strlen(IM_STRINGIFY((IM_ASSERT(true)))); + bool assert_maybe_disabled = (!assert_runs_expression || assert_expand_len <= 16); + ImGui::Text("IM_ASSERT: runs expression: %s. expand size: %s%s", + assert_runs_expression ? "OK" : "KO", (assert_expand_len > 16) ? "OK" : "KO", assert_maybe_disabled ? " (MAYBE DISABLED?!)" : ""); + if (assert_maybe_disabled) + { + ImGui::SameLine(); + HelpMarker("IM_ASSERT() calls assert() by default. Compiling with NDEBUG will usually strip out assert() to nothing, which is NOT recommended because we use asserts to notify of programmer mistakes!"); + } + ImGui::Separator(); ImGui::Text("io.BackendPlatformName: %s", io.BackendPlatformName ? io.BackendPlatformName : "NULL"); ImGui::Text("io.BackendRendererName: %s", io.BackendRendererName ? io.BackendRendererName : "NULL"); @@ -8181,22 +8277,34 @@ void ImGui::ShowAboutWindow(bool* p_open) //----------------------------------------------------------------------------- // Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options. -// Here we use the simplified Combo() api that packs items into a single literal string. -// Useful for quick combo boxes where the choices are known locally. bool ImGui::ShowStyleSelector(const char* label) { + // FIXME: This is a bit tricky to get right as style are functions, they don't register a name nor the fact that one is active. + // So we keep track of last active one among our limited selection. static int style_idx = -1; - if (ImGui::Combo(label, &style_idx, "Dark\0Light\0Classic\0")) + const char* style_names[] = { "Dark", "Light", "Classic" }; + bool ret = false; + if (ImGui::BeginCombo(label, (style_idx >= 0 && style_idx < IM_COUNTOF(style_names)) ? style_names[style_idx] : "")) { - switch (style_idx) + for (int n = 0; n < IM_COUNTOF(style_names); n++) { - case 0: ImGui::StyleColorsDark(); break; - case 1: ImGui::StyleColorsLight(); break; - case 2: ImGui::StyleColorsClassic(); break; + if (ImGui::Selectable(style_names[n], style_idx == n, ImGuiSelectableFlags_SelectOnNav)) + { + style_idx = n; + ret = true; + switch (style_idx) + { + case 0: ImGui::StyleColorsDark(); break; + case 1: ImGui::StyleColorsLight(); break; + case 2: ImGui::StyleColorsClassic(); break; + } + } + else if (style_idx == n) + ImGui::SetItemDefaultFocus(); } - return true; + ImGui::EndCombo(); } - return false; + return ret; } static const char* GetTreeLinesFlagsName(ImGuiTreeNodeFlags flags) @@ -8230,7 +8338,12 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) // General SeparatorText("General"); if ((GetIO().BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0) + { BulletText("Warning: Font scaling will NOT be smooth, because\nImGuiBackendFlags_RendererHasTextures is not set!"); + BulletText("For instructions, see:"); + SameLine(); + TextLinkOpenURL("docs/BACKENDS.md", "https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md"); + } if (ShowStyleSelector("Colors##Selector")) ref_saved_style = style; @@ -8240,7 +8353,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) SameLine(0.0f, 0.0f); Text(" (out %.2f)", GetFontSize()); DragFloat("FontScaleMain", &style.FontScaleMain, 0.02f, 0.5f, 4.0f); //BeginDisabled(GetIO().ConfigDpiScaleFonts); - DragFloat("FontScaleDpi", &style.FontScaleDpi, 0.02f, 0.5f, 5.0f); + DragFloat("FontScaleDpi", &style.FontScaleDpi, 0.02f, 0.5f, 4.0f); //SetItemTooltip("When io.ConfigDpiScaleFonts is set, this value is automatically overwritten."); //EndDisabled(); @@ -8277,7 +8390,6 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f"); SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f"); SliderFloat("IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f, "%.0f"); - SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f"); SliderFloat("GrabMinSize", &style.GrabMinSize, 1.0f, 20.0f, "%.0f"); SeparatorText("Borders"); @@ -8291,16 +8403,22 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 12.0f, "%.0f"); SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f"); SliderFloat("PopupRounding", &style.PopupRounding, 0.0f, 12.0f, "%.0f"); - SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f"); SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f"); + SeparatorText("Scrollbar"); + SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f"); + SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f"); + SliderFloat("ScrollbarPadding", &style.ScrollbarPadding, 0.0f, 10.0f, "%.0f"); + SeparatorText("Tabs"); SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f, "%.0f"); SliderFloat("TabBarBorderSize", &style.TabBarBorderSize, 0.0f, 2.0f, "%.0f"); SliderFloat("TabBarOverlineSize", &style.TabBarOverlineSize, 0.0f, 3.0f, "%.0f"); SameLine(); HelpMarker("Overline is only drawn over the selected tab when ImGuiTabBarFlags_DrawSelectedOverline is set."); - DragFloat("TabCloseButtonMinWidthSelected", &style.TabCloseButtonMinWidthSelected, 0.1f, -1.0f, 100.0f, (style.TabCloseButtonMinWidthSelected < 0.0f) ? "%.0f (Always)" : "%.0f"); - DragFloat("TabCloseButtonMinWidthUnselected", &style.TabCloseButtonMinWidthUnselected, 0.1f, -1.0f, 100.0f, (style.TabCloseButtonMinWidthUnselected < 0.0f) ? "%.0f (Always)" : "%.0f"); + DragFloat("TabMinWidthBase", &style.TabMinWidthBase, 0.5f, 1.0f, 500.0f, "%.0f"); + DragFloat("TabMinWidthShrink", &style.TabMinWidthShrink, 0.5f, 1.0f, 500.0f, "%0.f"); + DragFloat("TabCloseButtonMinWidthSelected", &style.TabCloseButtonMinWidthSelected, 0.5f, -1.0f, 100.0f, (style.TabCloseButtonMinWidthSelected < 0.0f) ? "%.0f (Always)" : "%.0f"); + DragFloat("TabCloseButtonMinWidthUnselected", &style.TabCloseButtonMinWidthUnselected, 0.5f, -1.0f, 100.0f, (style.TabCloseButtonMinWidthUnselected < 0.0f) ? "%.0f (Always)" : "%.0f"); SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f, "%.0f"); SeparatorText("Tables"); @@ -8331,6 +8449,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) style.WindowMenuButtonPosition = (ImGuiDir)(window_menu_button_position - 1); SeparatorText("Widgets"); + SliderFloat("ColorMarkerSize", &style.ColorMarkerSize, 0.0f, 8.0f, "%.0f"); Combo("ColorButtonPosition", (int*)&style.ColorButtonPosition, "Left\0Right\0"); SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f"); SameLine(); HelpMarker("Alignment applies when a button is larger than its text content."); @@ -8383,7 +8502,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) } LogFinish(); } - SameLine(); SetNextItemWidth(120); Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); + SameLine(); SetNextItemWidth(GetFontSize() * 10); Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); SameLine(); Checkbox("Only Modified Colors", &output_only_modified); static ImGuiTextFilter filter; @@ -8439,7 +8558,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ShowFontAtlas(atlas); // Post-baking font scaling. Note that this is NOT the nice way of scaling fonts, read below. - // (we enforce hard clamping manually as by default DragFloat/SliderFloat allows CTRL+Click text to get out of bounds). + // (we enforce hard clamping manually as by default DragFloat/SliderFloat allows Ctrl+Click text to get out of bounds). /* SeparatorText("Legacy Scaling"); const float MIN_SCALE = 0.3f; @@ -8541,27 +8660,27 @@ void ImGui::ShowUserGuide() ImGuiIO& io = GetIO(); BulletText("Double-click on title bar to collapse window."); BulletText( - "Click and drag on lower corner to resize window\n" - "(double-click to auto fit window to its contents)."); - BulletText("CTRL+Click on a slider or drag box to input value as text."); - BulletText("TAB/SHIFT+TAB to cycle through keyboard editable fields."); - BulletText("CTRL+Tab to select a window."); + "Click and drag on lower corner or border to resize window.\n" + "(double-click to auto fit window to its contents)"); + BulletText("Ctrl+Click on a slider or drag box to input value as text."); + BulletText("Tab/Shift+Tab to cycle through keyboard editable fields."); + BulletText("Ctrl+Tab/Ctrl+Shift+Tab to focus windows."); if (io.FontAllowUserScaling) - BulletText("CTRL+Mouse Wheel to zoom window contents."); + BulletText("Ctrl+Mouse Wheel to zoom window contents."); BulletText("While inputting text:\n"); Indent(); - BulletText("CTRL+Left/Right to word jump."); - BulletText("CTRL+A or double-click to select all."); - BulletText("CTRL+X/C/V to use clipboard cut/copy/paste."); - BulletText("CTRL+Z to undo, CTRL+Y/CTRL+SHIFT+Z to redo."); - BulletText("ESCAPE to revert."); + BulletText("Ctrl+Left/Right to word jump."); + BulletText("Ctrl+A or double-click to select all."); + BulletText("Ctrl+X/C/V to use clipboard cut/copy/paste."); + BulletText("Ctrl+Z to undo, Ctrl+Y/Ctrl+Shift+Z to redo."); + BulletText("Escape to revert."); Unindent(); BulletText("With keyboard navigation enabled:"); Indent(); - BulletText("Arrow keys to navigate."); + BulletText("Arrow keys or Home/End/PageUp/PageDown to navigate."); BulletText("Space to activate a widget."); BulletText("Return to input text into a widget."); - BulletText("Escape to deactivate a widget, close popup, exit child window."); + BulletText("Escape to deactivate a widget, close popup,\nexit a child window or the menu layer, clear focus."); BulletText("Alt to jump to the menu layer of a window."); Unindent(); } @@ -8588,12 +8707,12 @@ static void ShowExampleAppMainMenuBar() } if (ImGui::BeginMenu("Edit")) { - if (ImGui::MenuItem("Undo", "CTRL+Z")) {} - if (ImGui::MenuItem("Redo", "CTRL+Y", false, false)) {} // Disabled item + if (ImGui::MenuItem("Undo", "Ctrl+Z")) {} + if (ImGui::MenuItem("Redo", "Ctrl+Y", false, false)) {} // Disabled item ImGui::Separator(); - if (ImGui::MenuItem("Cut", "CTRL+X")) {} - if (ImGui::MenuItem("Copy", "CTRL+C")) {} - if (ImGui::MenuItem("Paste", "CTRL+V")) {} + if (ImGui::MenuItem("Cut", "Ctrl+X")) {} + if (ImGui::MenuItem("Copy", "Ctrl+C")) {} + if (ImGui::MenuItem("Paste", "Ctrl+V")) {} ImGui::EndMenu(); } ImGui::EndMainMenuBar(); @@ -8742,8 +8861,8 @@ struct ExampleAppConsole char buf[1024]; va_list args; va_start(args, fmt); - vsnprintf(buf, IM_ARRAYSIZE(buf), fmt, args); - buf[IM_ARRAYSIZE(buf)-1] = 0; + vsnprintf(buf, IM_COUNTOF(buf), fmt, args); + buf[IM_COUNTOF(buf)-1] = 0; va_end(args); Items.push_back(Strdup(buf)); } @@ -8871,7 +8990,7 @@ struct ExampleAppConsole // Command-line bool reclaim_focus = false; ImGuiInputTextFlags input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_EscapeClearsAll | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory; - if (ImGui::InputText("Input", InputBuf, IM_ARRAYSIZE(InputBuf), input_text_flags, &TextEditCallbackStub, (void*)this)) + if (ImGui::InputText("Input", InputBuf, IM_COUNTOF(InputBuf), input_text_flags, &TextEditCallbackStub, (void*)this)) { char* s = InputBuf; Strtrim(s); @@ -9195,8 +9314,8 @@ static void ShowExampleAppLog(bool* p_open) const char* words[] = { "Bumfuzzled", "Cattywampus", "Snickersnee", "Abibliophobia", "Absquatulate", "Nincompoop", "Pauciloquent" }; for (int n = 0; n < 5; n++) { - const char* category = categories[counter % IM_ARRAYSIZE(categories)]; - const char* word = words[counter % IM_ARRAYSIZE(words)]; + const char* category = categories[counter % IM_COUNTOF(categories)]; + const char* word = words[counter % IM_COUNTOF(words)]; log.AddLog("[%05d] [%s] Hello, current time is %.1f, here's a word: '%s'\n", ImGui::GetFrameCount(), category, ImGui::GetTime(), word); counter++; @@ -9235,10 +9354,9 @@ static void ShowExampleAppLayout(bool* p_open) ImGui::BeginChild("left pane", ImVec2(150, 0), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeX); for (int i = 0; i < 100; i++) { - // FIXME: Good candidate to use ImGuiSelectableFlags_SelectOnNav char label[128]; sprintf(label, "MyObject %d", i); - if (ImGui::Selectable(label, selected == i)) + if (ImGui::Selectable(label, selected == i, ImGuiSelectableFlags_SelectOnNav)) selected = i; } ImGui::EndChild(); @@ -9298,7 +9416,7 @@ struct ExampleAppPropertyEditor ImGui::SetNextItemWidth(-FLT_MIN); ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip); ImGui::PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true); - if (ImGui::InputTextWithHint("##Filter", "incl,-excl", Filter.InputBuf, IM_ARRAYSIZE(Filter.InputBuf), ImGuiInputTextFlags_EscapeClearsAll)) + if (ImGui::InputTextWithHint("##Filter", "incl,-excl", Filter.InputBuf, IM_COUNTOF(Filter.InputBuf), ImGuiInputTextFlags_EscapeClearsAll)) Filter.Build(); ImGui::PopItemFlag(); @@ -9590,7 +9708,7 @@ static void ShowExampleAppConstrainedResize(bool* p_open) IMGUI_DEMO_MARKER("Examples/Constrained Resizing window"); if (ImGui::GetIO().KeyShift) { - // Display a dummy viewport (in your real app you would likely use ImageButton() to display a texture. + // Display a dummy viewport (in your real app you would likely use ImageButton() to display a texture) ImVec2 avail_size = ImGui::GetContentRegionAvail(); ImVec2 pos = ImGui::GetCursorScreenPos(); ImGui::ColorButton("viewport", ImVec4(0.5f, 0.2f, 0.5f, 1.0f), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, avail_size); @@ -9599,12 +9717,12 @@ static void ShowExampleAppConstrainedResize(bool* p_open) } else { - ImGui::Text("(Hold SHIFT to display a dummy viewport)"); + ImGui::Text("(Hold Shift to display a dummy viewport)"); if (ImGui::Button("Set 200x200")) { ImGui::SetWindowSize(ImVec2(200, 200)); } ImGui::SameLine(); if (ImGui::Button("Set 500x500")) { ImGui::SetWindowSize(ImVec2(500, 500)); } ImGui::SameLine(); if (ImGui::Button("Set 800x200")) { ImGui::SetWindowSize(ImVec2(800, 200)); } ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20); - ImGui::Combo("Constraint", &type, test_desc, IM_ARRAYSIZE(test_desc)); + ImGui::Combo("Constraint", &type, test_desc, IM_COUNTOF(test_desc)); ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20); ImGui::DragInt("Lines", &display_lines, 0.2f, 1, 100); ImGui::Checkbox("Auto-resize", &auto_resize); @@ -9846,7 +9964,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) draw_list->AddTriangle(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col, th);x += sz + spacing; // Triangle //draw_list->AddTriangle(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col, th);x+= sz*0.4f + spacing; // Thin triangle PathConcaveShape(draw_list, x, y, sz); draw_list->PathStroke(col, ImDrawFlags_Closed, th); x += sz + spacing; // Concave Shape - //draw_list->AddPolyline(concave_shape, IM_ARRAYSIZE(concave_shape), col, ImDrawFlags_Closed, th); + //draw_list->AddPolyline(concave_shape, IM_COUNTOF(concave_shape), col, ImDrawFlags_Closed, th); draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!) draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!) draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col, th); x += sz + spacing; // Diagonal line @@ -10296,7 +10414,7 @@ void ShowExampleAppDocuments(bool* p_open) if (ImGui::BeginPopup("Rename")) { ImGui::SetNextItemWidth(ImGui::GetFontSize() * 30); - if (ImGui::InputText("###Name", app.RenamingDoc->Name, IM_ARRAYSIZE(app.RenamingDoc->Name), ImGuiInputTextFlags_EnterReturnsTrue)) + if (ImGui::InputText("###Name", app.RenamingDoc->Name, IM_COUNTOF(app.RenamingDoc->Name), ImGuiInputTextFlags_EnterReturnsTrue)) { ImGui::CloseCurrentPopup(); app.RenamingDoc = NULL; @@ -10416,7 +10534,7 @@ struct ExampleAsset if (delta < 0) return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1 : +1; } - return ((int)a->ID - (int)b->ID); + return (int)a->ID - (int)b->ID; } }; const ImGuiTableSortSpecs* ExampleAsset::s_current_sort_specs = NULL; @@ -10536,7 +10654,7 @@ struct ExampleAssetsBrowser ImGui::SeparatorText("Layout"); ImGui::SliderFloat("Icon Size", &IconSize, 16.0f, 128.0f, "%.0f"); - ImGui::SameLine(); HelpMarker("Use CTRL+Wheel to zoom"); + ImGui::SameLine(); HelpMarker("Use Ctrl+Wheel to zoom"); ImGui::SliderInt("Icon Spacing", &IconSpacing, 0, 32); ImGui::SliderInt("Icon Hit Spacing", &IconHitSpacing, 0, 32); ImGui::Checkbox("Stretch Spacing", &StretchSpacing); @@ -10694,7 +10812,7 @@ struct ExampleAssetsBrowser draw_list->AddRectFilled(box_min, box_max, icon_bg_color); // Background color if (ShowTypeOverlay && item_data->Type != 0) { - ImU32 type_col = icon_type_overlay_colors[item_data->Type % IM_ARRAYSIZE(icon_type_overlay_colors)]; + ImU32 type_col = icon_type_overlay_colors[item_data->Type % IM_COUNTOF(icon_type_overlay_colors)]; draw_list->AddRectFilled(ImVec2(box_max.x - 2 - icon_type_overlay_size.x, box_min.y + 2), ImVec2(box_max.x - 2, box_min.y + 2 + icon_type_overlay_size.y), type_col); } if (display_label) @@ -10728,7 +10846,7 @@ struct ExampleAssetsBrowser if (want_delete) Selection.ApplyDeletionPostLoop(ms_io, Items, item_curr_idx_to_focus); - // Zooming with CTRL+Wheel + // Zooming with Ctrl+Wheel if (ImGui::IsWindowAppearing()) ZoomWheelAccum = 0.0f; if (ImGui::IsWindowHovered() && io.MouseWheel != 0.0f && ImGui::IsKeyDown(ImGuiMod_Ctrl) && ImGui::IsAnyItemActive() == false) diff --git a/dep/imgui/src/imgui_draw.cpp b/dep/imgui/src/imgui_draw.cpp index 1abae9b91..d1b705575 100644 --- a/dep/imgui/src/imgui_draw.cpp +++ b/dep/imgui/src/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.0 +// dear imgui, v1.92.6 WIP // (drawing and font code) /* @@ -17,10 +17,11 @@ Index of this file: // [SECTION] ImFontAtlas: backend for stb_truetype // [SECTION] ImFontAtlas: glyph ranges helpers // [SECTION] ImFontGlyphRangesBuilder -// [SECTION] ImFont +// [SECTION] ImFontBaked, ImFont // [SECTION] ImGui Internal Render Helpers // [SECTION] Decompression code // [SECTION] Default font data (ProggyClean.ttf) +// [SECTION] Default font data (ProggyVector-minimal.ttf) */ @@ -48,7 +49,7 @@ Index of this file: #pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen #pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). -#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer) +#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). #endif // Clang/GCC warnings with -Weverything @@ -240,6 +241,8 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst) colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); colors[ImGuiCol_TreeLines] = colors[ImGuiCol_Border]; colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); + colors[ImGuiCol_DragDropTargetBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); + colors[ImGuiCol_UnsavedMarker] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_NavCursor] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); @@ -305,6 +308,8 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst) colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f); colors[ImGuiCol_TreeLines] = colors[ImGuiCol_Border]; colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); + colors[ImGuiCol_DragDropTargetBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); + colors[ImGuiCol_UnsavedMarker] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); colors[ImGuiCol_NavCursor] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); @@ -371,6 +376,8 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); colors[ImGuiCol_TreeLines] = colors[ImGuiCol_Border]; colors[ImGuiCol_DragDropTarget] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); + colors[ImGuiCol_DragDropTargetBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); + colors[ImGuiCol_UnsavedMarker] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImGuiCol_NavCursor] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_NavWindowingHighlight] = ImVec4(0.70f, 0.70f, 0.70f, 0.70f); colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.20f); @@ -385,9 +392,9 @@ ImDrawListSharedData::ImDrawListSharedData() { memset(this, 0, sizeof(*this)); InitialFringeScale = 1.0f; - for (int i = 0; i < IM_ARRAYSIZE(ArcFastVtx); i++) + for (int i = 0; i < IM_COUNTOF(ArcFastVtx); i++) { - const float a = ((float)i * 2 * IM_PI) / (float)IM_ARRAYSIZE(ArcFastVtx); + 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); @@ -405,7 +412,7 @@ void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error) IM_ASSERT(max_error > 0.0f); CircleSegmentMaxError = max_error; - for (int i = 0; i < IM_ARRAYSIZE(CircleSegmentCounts); i++) + 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); @@ -438,10 +445,13 @@ void ImDrawList::_SetDrawListSharedData(ImDrawListSharedData* data) // In the majority of cases, you would want to call PushClipRect() and PushTexture() after this. void ImDrawList::_ResetForNewFrame() { - // Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory. + // Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory to match ImDrawCmdHeader. IM_STATIC_ASSERT(offsetof(ImDrawCmd, ClipRect) == 0); IM_STATIC_ASSERT(offsetof(ImDrawCmd, TexRef) == sizeof(ImVec4)); IM_STATIC_ASSERT(offsetof(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureRef)); + IM_STATIC_ASSERT(offsetof(ImDrawCmd, ClipRect) == offsetof(ImDrawCmdHeader, ClipRect)); + IM_STATIC_ASSERT(offsetof(ImDrawCmd, TexRef) == offsetof(ImDrawCmdHeader, TexRef)); + IM_STATIC_ASSERT(offsetof(ImDrawCmd, VtxOffset) == offsetof(ImDrawCmdHeader, VtxOffset)); if (_Splitter._Count > 1) _Splitter.Merge(this); @@ -478,9 +488,10 @@ void ImDrawList::_ClearFreeMemory() _Splitter.ClearFreeMemory(); } +// Note: For multi-threaded rendering, consider using `imgui_threaded_rendering` from https://github.com/ocornut/imgui_club ImDrawList* ImDrawList::CloneOutput() const { - ImDrawList* dst = IM_NEW(ImDrawList(_Data)); + ImDrawList* dst = IM_NEW(ImDrawList(NULL)); dst->CmdBuffer = CmdBuffer; dst->IdxBuffer = IdxBuffer; dst->VtxBuffer = VtxBuffer; @@ -517,6 +528,7 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* userdata, size_t use { IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; + IM_ASSERT(callback != NULL); IM_ASSERT(curr_cmd->UserCallback == NULL); if (curr_cmd->ElemCount != 0) { @@ -635,7 +647,7 @@ int ImDrawList::_CalcCircleAutoSegmentCount(float radius) const { // Automatic segment count const int radius_idx = (int)(radius + 0.999999f); // ceil to never reduce accuracy - if (radius_idx >= 0 && radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts)) + 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); @@ -819,7 +831,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 const bool use_texture = (Flags & ImDrawListFlags_AntiAliasedLinesUseTex) && (integer_thickness < IM_DRAWLIST_TEX_LINES_WIDTH_MAX) && (fractional_thickness <= 0.00001f) && (AA_SIZE == 1.0f); // We should never hit this, because NewFrame() doesn't set ImDrawListFlags_AntiAliasedLinesUseTex unless ImFontAtlasFlags_NoBakedLines is off - IM_ASSERT_PARANOID(!use_texture || !(_Data->Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines)); + IM_ASSERT_PARANOID(!use_texture || !(_Data->Font->OwnerAtlas->Flags & ImFontAtlasFlags_NoBakedLines)); const int idx_count = use_texture ? (count * 6) : (thick_line ? count * 18 : count * 12); const int vtx_count = use_texture ? (points_count * 2) : (thick_line ? points_count * 4 : points_count * 3); @@ -1719,7 +1731,7 @@ void ImDrawList::AddText(ImFont* font, float font_size, float font_weight, const clip_rect.z = ImMin(clip_rect.z, cpu_fine_clip_rect->z); clip_rect.w = ImMin(clip_rect.w, cpu_fine_clip_rect->w); } - font->RenderText(this, font_size, font_weight, pos, col, clip_rect, text_begin, text_end, wrap_width, cpu_fine_clip_rect != NULL); + font->RenderText(this, font_size, font_weight, pos, col, clip_rect, text_begin, text_end, wrap_width, (cpu_fine_clip_rect != NULL) ? ImDrawTextFlags_CpuFineClip : ImDrawTextFlags_None); } void ImDrawList::AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end) @@ -2314,17 +2326,16 @@ void ImDrawData::DeIndexAllBuffers() { ImVector new_vtx_buffer; TotalVtxCount = TotalIdxCount = 0; - for (int i = 0; i < CmdListsCount; i++) + for (ImDrawList* draw_list : CmdLists) { - ImDrawList* cmd_list = CmdLists[i]; - if (cmd_list->IdxBuffer.empty()) + if (draw_list->IdxBuffer.empty()) continue; - new_vtx_buffer.resize(cmd_list->IdxBuffer.Size); - for (int j = 0; j < cmd_list->IdxBuffer.Size; j++) - new_vtx_buffer[j] = cmd_list->VtxBuffer[cmd_list->IdxBuffer[j]]; - cmd_list->VtxBuffer.swap(new_vtx_buffer); - cmd_list->IdxBuffer.resize(0); - TotalVtxCount += cmd_list->VtxBuffer.Size; + new_vtx_buffer.resize(draw_list->IdxBuffer.Size); + for (int j = 0; j < draw_list->IdxBuffer.Size; j++) + new_vtx_buffer[j] = draw_list->VtxBuffer[draw_list->IdxBuffer[j]]; + draw_list->VtxBuffer.swap(new_vtx_buffer); + draw_list->IdxBuffer.resize(0); + TotalVtxCount += draw_list->VtxBuffer.Size; } } @@ -2410,6 +2421,7 @@ ImFontConfig::ImFontConfig() FontDataOwnedByAtlas = true; OversampleH = 0; // Auto == 1 or 2 depending on size OversampleV = 0; // Auto == 1 + ExtraSizeScale = 1.0f; GlyphMaxAdvanceX = FLT_MAX; RasterizerMultiply = 1.0f; RasterizerDensity = 1.0f; @@ -2459,8 +2471,10 @@ const char* ImTextureDataGetFormatName(ImTextureFormat format) void ImTextureData::Create(ImTextureFormat format, int w, int h) { + IM_ASSERT(Status == ImTextureStatus_Destroyed); DestroyPixels(); Format = format; + Status = ImTextureStatus_WantCreate; Width = w; Height = h; BytesPerPixel = ImTextureDataGetFormatBytesPerPixel(format); @@ -2506,6 +2520,8 @@ void ImTextureData::DestroyPixels() //----------------------------------------------------------------------------- // - ImFontAtlas::AddFont() // - ImFontAtlas::AddFontDefault() +// - ImFontAtlas::AddFontDefaultBitmap() +// - ImFontAtlas::AddFontDefaultVector() // - ImFontAtlas::AddFontFromFileTTF() // - ImFontAtlas::AddFontFromMemoryTTF() // - ImFontAtlas::AddFontFromMemoryCompressedTTF() @@ -2562,6 +2578,7 @@ void ImTextureData::DestroyPixels() //----------------------------------------------------------------------------- // - ImFontBaked_BuildGrowIndex() // - ImFontBaked_BuildLoadGlyph() +// - ImFontBaked_BuildLoadGlyphAdvanceX() // - ImFontAtlasDebugLogTextureRequests() //----------------------------------------------------------------------------- // - ImFontAtlasGetFontLoaderForStbTruetype() @@ -2631,6 +2648,7 @@ ImFontAtlas::ImFontAtlas() TexMinHeight = 128; TexMaxWidth = 8192; TexMaxHeight = 8192; + TexRef._TexID = ImTextureID_Invalid; RendererHasTextures = false; // Assumed false by default, as apps can call e.g Atlas::Build() after backend init and before ImGui can update. TexNextUniqueID = 1; FontNextUniqueID = 1; @@ -2647,6 +2665,7 @@ ImFontAtlas::~ImFontAtlas() TexData = NULL; } +// If you call this mid-frame, you would need to add new font and bind them! void ImFontAtlas::Clear() { bool backup_renderer_has_textures = RendererHasTextures; @@ -2661,6 +2680,11 @@ void ImFontAtlas::CompactCache() ImFontAtlasTextureCompact(this); } +void ImFontAtlas::SetFontLoader(const ImFontLoader* font_loader) +{ + ImFontAtlasBuildSetupFontLoader(this, font_loader); +} + void ImFontAtlas::ClearInputData() { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!"); @@ -2692,6 +2716,8 @@ void ImFontAtlas::ClearFonts() { // FIXME-NEWATLAS: Illegal to remove currently bound font. IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!"); + for (ImFont* font : Fonts) + ImFontAtlasBuildNotifySetFont(this, font, NULL); ImFontAtlasBuildDestroy(this); ClearInputData(); Fonts.clear_delete(); @@ -2735,10 +2761,9 @@ void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool rendere if (atlas->Builder == NULL) // This will only happen if fonts were not already loaded. ImFontAtlasBuildMain(atlas); } - else // Legacy backend - { + // Legacy backend + if (!atlas->RendererHasTextures) IM_ASSERT_USER_ERROR(atlas->TexIsBuilt, "Backend does not support ImGuiBackendFlags_RendererHasTextures, and font atlas is not built! Update backend OR make sure you called ImGui_ImplXXXX_NewFrame() function for renderer backend, which should call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()."); - } if (atlas->TexIsBuilt && atlas->Builder->PreloadedAllGlyphsRanges) IM_ASSERT_USER_ERROR(atlas->RendererHasTextures == false, "Called ImFontAtlas::Build() before ImGuiBackendFlags_RendererHasTextures got set! With new backends: you don't need to call Build()."); @@ -2783,20 +2808,29 @@ void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool rendere 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 (e.g. freed resources mid-run) - } - else if (tex->WantDestroyNextFrame && tex->Status != ImTextureStatus_WantDestroy) - { - // Request destroy. Keep bool as it allows us to keep track of things. - // We don't destroy pixels right away, as backend may have an in-flight copy from RAM. - IM_ASSERT(tex->Status == ImTextureStatus_OK || tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates); - tex->Status = ImTextureStatus_WantDestroy; + 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. @@ -2804,13 +2838,10 @@ void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool rendere if (tex->Status == ImTextureStatus_WantDestroy) tex->UnusedFrames++; - // If a texture has never reached the backend, they don't need to know about it. - if (tex->Status == ImTextureStatus_WantDestroy && tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL) - remove_from_list = true; - // 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); @@ -2950,7 +2981,7 @@ void ImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas, ImTextureData* tex, tex->UsedRect.h = (unsigned short)(ImMax(tex->UsedRect.y + tex->UsedRect.h, req.y + req.h) - tex->UsedRect.y); atlas->TexIsBuilt = false; - // No need to queue if status is _WantCreate + // No need to queue if status is == ImTextureStatus_WantCreate if (tex->Status == ImTextureStatus_OK || tex->Status == ImTextureStatus_WantUpdates) { tex->Status = ImTextureStatus_WantUpdates; @@ -3019,8 +3050,8 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg_in) } else { - IM_ASSERT(Fonts.Size > 0 && "Cannot use MergeMode for the first font"); // When using MergeMode make sure that a font has already been added before. You can use ImGui::GetIO().Fonts->AddFontDefault() to add the default imgui font. - font = Fonts.back(); + IM_ASSERT(Fonts.Size > 0 && "Cannot use MergeMode for the first font"); // When using MergeMode make sure that a font has already been added before. + font = font_cfg_in->DstFont ? font_cfg_in->DstFont : Fonts.back(); } // Add to list @@ -3031,15 +3062,6 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg_in) font->Sources.push_back(font_cfg); ImFontAtlasBuildUpdatePointers(this); // Pointers to Sources are otherwise dangling after we called Sources.push_back(). -#if 0 - // DUCKSTATION-CHANGE: We maintain the allocations ourselves. Saves copying 14MB or so. - if (font_cfg->FontDataOwnedByAtlas == false) - { - font_cfg->FontDataOwnedByAtlas = true; - font_cfg->FontData = ImMemdup(font_cfg->FontData, (size_t)font_cfg->FontDataSize); - } -#endif - // Sanity check // We don't round cfg.SizePixels yet as relative size of merged fonts are used afterwards. if (font_cfg->GlyphExcludeRanges != NULL) @@ -3090,11 +3112,30 @@ static void Decode85(const unsigned char* src, unsigned char* dst) } } #ifndef IMGUI_DISABLE_DEFAULT_FONT -static const char* GetDefaultCompressedFontDataTTF(int* out_size); +static const char* GetDefaultCompressedFontDataProggyClean(int* out_size); +static const char* GetDefaultCompressedFontDataProggyVector(int* out_size); #endif -// Load embedded ProggyClean.ttf at size 13, disable oversampling -ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template) +// 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. +static float GetExpectedContextFontSize(ImGuiContext* ctx) +{ + return ((ctx->Style.FontSizeBase > 0.0f) ? ctx->Style.FontSizeBase : 13.0f) * ctx->Style.FontScaleMain * ctx->Style.FontScaleDpi; +} + +// Legacy function with heuristic to select Pixel or Vector font. +// The selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold at the time of adding the default font. +// Prefer calling AddFontDefaultVector() or AddFontDefaultBitmap() based on your own logic. +ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg) +{ + if (OwnerContext == NULL || GetExpectedContextFontSize(OwnerContext) >= 16.0f) + return AddFontDefaultVector(font_cfg); + else + return AddFontDefaultBitmap(font_cfg); +} + +// Load embedded ProggyClean.ttf. Default size 13, disable oversampling. +// 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 ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); @@ -3104,19 +3145,40 @@ ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template) font_cfg.PixelSnapH = true; } if (font_cfg.SizePixels <= 0.0f) - font_cfg.SizePixels = 13.0f * 1.0f; + font_cfg.SizePixels = 13.0f; // This only serves (1) as a reference for GlyphOffset.y setting and (2) as a default for pre-1.92 backend. if (font_cfg.Name[0] == '\0') - ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "ProggyClean.ttf"); + ImFormatString(font_cfg.Name, IM_COUNTOF(font_cfg.Name), "ProggyClean.ttf"); font_cfg.EllipsisChar = (ImWchar)0x0085; - font_cfg.GlyphOffset.y += 1.0f * IM_TRUNC(font_cfg.SizePixels / 13.0f); // Add +1 offset per 13 units + 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 = GetDefaultCompressedFontDataTTF(&ttf_compressed_size); - const ImWchar* glyph_ranges = font_cfg.GlyphRanges != NULL ? font_cfg.GlyphRanges : GetGlyphRangesDefault(); - ImFont* font = AddFontFromMemoryCompressedTTF(ttf_compressed, ttf_compressed_size, font_cfg.SizePixels, &font_cfg, glyph_ranges); - return font; + const char* ttf_compressed = GetDefaultCompressedFontDataProggyClean(&ttf_compressed_size); + return AddFontFromMemoryCompressedTTF(ttf_compressed, ttf_compressed_size, font_cfg.SizePixels, &font_cfg); #else - IM_ASSERT(0 && "AddFontDefault() disabled in this build."); + IM_ASSERT(0 && "Function is disabled in this build."); + IM_UNUSED(font_cfg_template); + return NULL; +#endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT +} + +// Load a minimal version of ProggyVector, designed to match our good old ProggyClean, but nicely scalable. +// (See misc/fonts/ProggyVector-ExtractScript.txt for details) +ImFont* ImFontAtlas::AddFontDefaultVector(const ImFontConfig* font_cfg_template) +{ +#ifndef IMGUI_DISABLE_DEFAULT_FONT + ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); + if (font_cfg.SizePixels <= 0.0f) + font_cfg.SizePixels = 16.0f; + if (font_cfg.Name[0] == '\0') + ImFormatString(font_cfg.Name, IM_COUNTOF(font_cfg.Name), "ProggyVector.ttf"); + font_cfg.ExtraSizeScale = 1.140f; // Match ProggyClean + font_cfg.GlyphOffset.y += -0.5f * (font_cfg.SizePixels / 13.0f); // Closer match ProggyClean + avoid descenders going too high (with current code). + + int ttf_compressed_size = 0; + const char* ttf_compressed = GetDefaultCompressedFontDataProggyVector(&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 @@ -3142,7 +3204,7 @@ ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, // Store a short copy of filename into into the font name for convenience const char* p; for (p = filename + ImStrlen(filename); p > filename && p[-1] != '/' && p[-1] != '\\'; p--) {} - ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "%s", p); + ImFormatString(font_cfg.Name, IM_COUNTOF(font_cfg.Name), "%s", p); } return AddFontFromMemoryTTF(data, (int)data_size, size_pixels, weight, &font_cfg, glyph_ranges); } @@ -3187,7 +3249,7 @@ ImFont* ImFontAtlas::AddFontFromMemoryCompressedBase85TTF(const char* compressed // On font removal we need to remove references (otherwise we could queue removal?) // We allow old_font == new_font which forces updating all values (e.g. sizes) -static void ImFontAtlasBuildNotifySetFont(ImFontAtlas* atlas, ImFont* old_font, ImFont* new_font) +void ImFontAtlasBuildNotifySetFont(ImFontAtlas* atlas, ImFont* old_font, ImFont* new_font) { for (ImDrawListSharedData* shared_data : atlas->DrawListSharedDatas) { @@ -3217,7 +3279,6 @@ static void ImFontAtlasBuildNotifySetFont(ImFontAtlas* atlas, ImFont* old_font, void ImFontAtlas::RemoveFont(ImFont* font) { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!"); - font->ClearOutputData(); ImFontAtlasFontDestroyOutput(this, font); for (ImFontConfig* src : font->Sources) @@ -3232,7 +3293,7 @@ void ImFontAtlas::RemoveFont(ImFont* font) ImFontAtlasBuildUpdatePointers(this); - font->ContainerAtlas = NULL; + font->OwnerAtlas = NULL; IM_DELETE(font); // Notify external systems @@ -3363,11 +3424,7 @@ void ImFontAtlasBuildMain(ImFontAtlas* atlas) { IM_ASSERT(!atlas->Locked && "Cannot modify a locked ImFontAtlas!"); if (atlas->TexData && atlas->TexData->Format != atlas->TexDesiredFormat) - { - ImVec2i new_tex_size = ImFontAtlasTextureGetSizeEstimate(atlas); - ImFontAtlasBuildDestroy(atlas); - ImFontAtlasTextureAdd(atlas, new_tex_size.x, new_tex_size.y); - } + ImFontAtlasBuildClear(atlas); if (atlas->Builder == NULL) ImFontAtlasBuildInit(atlas); @@ -3385,6 +3442,7 @@ void ImFontAtlasBuildMain(ImFontAtlas* atlas) void ImFontAtlasBuildGetOversampleFactors(ImFontConfig* src, ImFontBaked* baked, int* out_oversample_h, int* out_oversample_v) { + // (Only used by stb_truetype builder) // Automatically disable horizontal oversampling over size 36 const float raster_size = baked->Size * baked->RasterizerDensity * src->RasterizerDensity; *out_oversample_h = (src->OversampleH != 0) ? src->OversampleH : (raster_size > 36.0f || src->PixelSnapH) ? 1 : 2; @@ -3412,6 +3470,9 @@ void ImFontAtlasBuildSetupFontLoader(ImFontAtlas* atlas, const ImFontLoader* fon atlas->FontLoader->LoaderInit(atlas); for (ImFont* font : atlas->Fonts) ImFontAtlasFontInitOutput(atlas, font); + for (ImFont* font : atlas->Fonts) + for (ImFontConfig* src : font->Sources) + ImFontAtlasFontSourceAddToFont(atlas, font, src); } // Preload all glyph ranges for legacy backends. @@ -3578,11 +3639,8 @@ bool ImFontAtlasFontInitOutput(ImFontAtlas* atlas, ImFont* font) { bool ret = true; for (ImFontConfig* src : font->Sources) - { - const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader; - if (loader && loader->FontSrcInit != NULL && !loader->FontSrcInit(atlas, src)) + if (!ImFontAtlasFontSourceInit(atlas, src)) ret = false; - } IM_ASSERT(ret); // Unclear how to react to this meaningfully. Assume that result will be same as initial AddFont() call. return ret; } @@ -3599,6 +3657,12 @@ void ImFontAtlasFontDestroyOutput(ImFontAtlas* atlas, ImFont* font) } } +void ImFontAtlasFontRebuildOutput(ImFontAtlas* atlas, ImFont* font) +{ + ImFontAtlasFontDestroyOutput(atlas, font); + ImFontAtlasFontInitOutput(atlas, font); +} + //----------------------------------------------------------------------------------------------------------------------------- bool ImFontAtlasFontSourceInit(ImFontAtlas* atlas, ImFontConfig* src) @@ -3615,7 +3679,7 @@ void ImFontAtlasFontSourceAddToFont(ImFontAtlas* atlas, ImFont* font, ImFontConf { font->ClearOutputData(); //font->FontSize = src->SizePixels; - font->ContainerAtlas = atlas; + font->OwnerAtlas = atlas; IM_ASSERT(font->Sources[0] == src); } atlas->TexIsBuilt = false; // For legacy backends @@ -3625,6 +3689,11 @@ void ImFontAtlasFontSourceAddToFont(ImFontAtlas* atlas, ImFont* font, ImFontConf void ImFontAtlasFontDestroySourceData(ImFontAtlas* atlas, ImFontConfig* src) { IM_UNUSED(atlas); + // IF YOU GET A CRASH IN THE IM_FREE() CALL HERE AND USED AddFontFromMemoryTTF(): + // - DUE TO LEGACY REASON AddFontFromMemoryTTF() TRANSFERS MEMORY OWNERSHIP BY DEFAULT. + // - IT WILL THEREFORE CRASH WHEN PASSED DATA WHICH MAY NOT BE FREEED BY IMGUI. + // - USE `ImFontConfig font_cfg; font_cfg.FontDataOwnedByAtlas = false; io.Fonts->AddFontFromMemoryTTF(....., &cfg);` to disable passing ownership/ + // WE WILL ADDRESS THIS IN A FUTURE REWORK OF THE API. if (src->FontDataOwnedByAtlas) IM_FREE(src->FontData); src->FontData = NULL; @@ -3638,7 +3707,7 @@ void ImFontAtlasFontDestroySourceData(ImFontAtlas* atlas, ImFontConfig* src) // FIXME-NEWATLAS: This borrows too much from FontLoader's FontLoadGlyph() handlers and suggest that we should add further helpers. static ImFontGlyph* ImFontAtlasBuildSetupFontBakedEllipsis(ImFontAtlas* atlas, ImFontBaked* baked) { - ImFont* font = baked->ContainerFont; + ImFont* font = baked->OwnerFont; IM_ASSERT(font->EllipsisChar != 0); const ImFontGlyph* dot_glyph = baked->FindGlyphNoFallback((ImWchar)'.'); @@ -3684,7 +3753,7 @@ static void ImFontAtlasBuildSetupFontBakedFallback(ImFontBaked* baked) { IM_ASSERT(baked->FallbackGlyphIndex == -1); IM_ASSERT(baked->FallbackAdvanceX == 0.0f); - ImFont* font = baked->ContainerFont; + ImFont* font = baked->OwnerFont; ImFontGlyph* fallback_glyph = NULL; if (font->FallbackChar != 0) fallback_glyph = baked->FindGlyphNoFallback(font->FallbackChar); @@ -3694,7 +3763,7 @@ static void ImFontAtlasBuildSetupFontBakedFallback(ImFontBaked* baked) ImFontGlyph glyph; glyph.Codepoint = 0; glyph.AdvanceX = space_glyph ? space_glyph->AdvanceX : IM_ROUND(baked->Size * 0.40f); - fallback_glyph = ImFontAtlasBakedAddFontGlyph(font->ContainerAtlas, baked, NULL, &glyph); + fallback_glyph = ImFontAtlasBakedAddFontGlyph(font->OwnerAtlas, baked, NULL, &glyph); } baked->FallbackGlyphIndex = baked->Glyphs.index_from_ptr(fallback_glyph); // Storing index avoid need to update pointer on growth and simplify inner loop code baked->FallbackAdvanceX = fallback_glyph->AdvanceX; @@ -3776,7 +3845,7 @@ ImFontBaked* ImFontAtlasBakedAdd(ImFontAtlas* atlas, ImFont* font, float font_si baked->RasterizerDensity = font_rasterizer_density; baked->Weight = font_weight; baked->BakedId = baked_id; - baked->ContainerFont = font; + baked->OwnerFont = font; baked->LastUsedFrame = atlas->Builder->FrameCount; // Initialize backend data @@ -3811,7 +3880,7 @@ ImFontBaked* ImFontAtlasBakedGetClosestMatch(ImFontAtlas* atlas, ImFont* font, f for (int baked_n = 0; baked_n < builder->BakedPool.Size; baked_n++) { ImFontBaked* baked = &builder->BakedPool[baked_n]; - if (baked->ContainerFont != font || baked->WantDestroy) + if (baked->OwnerFont != font || baked->WantDestroy) continue; if (baked->Weight != font_weight) continue; @@ -3869,7 +3938,7 @@ void ImFontAtlasFontDiscardBakes(ImFontAtlas* atlas, ImFont* font, int unused_fr ImFontBaked* baked = &builder->BakedPool[baked_n]; if (baked->LastUsedFrame + unused_frames > atlas->Builder->FrameCount) continue; - if (baked->ContainerFont != font || baked->WantDestroy) + if (baked->OwnerFont != font || baked->WantDestroy) continue; ImFontAtlasBakedDiscard(atlas, font, baked); } @@ -3884,9 +3953,9 @@ void ImFontAtlasBuildDiscardBakes(ImFontAtlas* atlas, int unused_frames) ImFontBaked* baked = &builder->BakedPool[baked_n]; if (baked->LastUsedFrame + unused_frames > atlas->Builder->FrameCount) continue; - if (baked->WantDestroy || (baked->ContainerFont->Flags & ImFontFlags_LockBakedSizes)) + if (baked->WantDestroy || (baked->OwnerFont->Flags & ImFontFlags_LockBakedSizes)) continue; - ImFontAtlasBakedDiscard(atlas, baked->ContainerFont, baked); + ImFontAtlasBakedDiscard(atlas, baked->OwnerFont, baked); } } @@ -3907,6 +3976,11 @@ void ImFontAtlasRemoveDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedDat void ImFontAtlasUpdateDrawListsTextures(ImFontAtlas* atlas, ImTextureRef old_tex, ImTextureRef new_tex) { for (ImDrawListSharedData* shared_data : atlas->DrawListSharedDatas) + { + // If Context 2 uses font owned by Context 1 which already called EndFrame()/Render(), we don't want to mess with draw commands for Context 1 + if (shared_data->Context && !shared_data->Context->WithinFrameScope) + continue; + for (ImDrawList* draw_list : shared_data->DrawLists) { // Replace in command-buffer @@ -3920,6 +3994,7 @@ void ImFontAtlasUpdateDrawListsTextures(ImFontAtlas* atlas, ImTextureRef old_tex if (stacked_tex == old_tex) stacked_tex = new_tex; } + } } // Update texture coordinates in all draw list shared context @@ -3976,7 +4051,6 @@ ImTextureData* ImFontAtlasTextureAdd(ImFontAtlas* atlas, int w, int h) } new_tex->Create(atlas->TexDesiredFormat, w, h); - new_tex->Status = ImTextureStatus_WantCreate; atlas->TexIsBuilt = false; ImFontAtlasBuildSetTexture(atlas, new_tex); @@ -3991,7 +4065,7 @@ static void ImFontAtlasDebugWriteTexToDisk(ImTextureData* tex, const char* descr { ImGuiContext& g = *GImGui; char buf[128]; - ImFormatString(buf, IM_ARRAYSIZE(buf), "[%05d] Texture #%03d - %s.png", g.FrameCount, tex->UniqueID, description); + ImFormatString(buf, IM_COUNTOF(buf), "[%05d] Texture #%03d - %s.png", g.FrameCount, tex->UniqueID, description); stbi_write_png(buf, tex->Width, tex->Height, tex->BytesPerPixel, tex->Pixels, tex->GetPitch()); // tex->BytesPerPixel is technically not component, but ok for the formats we support. } #endif @@ -4189,9 +4263,9 @@ void ImFontAtlasBuildInit(ImFontAtlas* atlas) if (atlas->FontLoader == NULL) { #ifdef IMGUI_ENABLE_FREETYPE - ImFontAtlasBuildSetupFontLoader(atlas, ImGuiFreeType::GetFontLoader()); + atlas->SetFontLoader(ImGuiFreeType::GetFontLoader()); #elif defined(IMGUI_ENABLE_STB_TRUETYPE) - ImFontAtlasBuildSetupFontLoader(atlas, ImFontAtlasGetFontLoaderForStbTruetype()); + atlas->SetFontLoader(ImFontAtlasGetFontLoaderForStbTruetype()); #else IM_ASSERT(0); // Invalid Build function #endif @@ -4220,6 +4294,10 @@ void ImFontAtlasBuildInit(ImFontAtlas* atlas) ImFontAtlasUpdateDrawListsSharedData(atlas); //atlas->TexIsBuilt = true; + + // Lazily initialize char/text classifier + // FIXME: This could be practically anywhere, and should eventually be parameters to CalcTextSize/word-wrapping code, but there's no obvious spot now. + ImTextInitClassifiers(); } // Destroy builder and all cached glyphs. Do not destroy actual fonts. @@ -4301,6 +4379,8 @@ void ImFontAtlasPackDiscardRect(ImFontAtlas* atlas, ImFontAtlasRectId id) index_entry->IsUsed = false; index_entry->TargetIndex = builder->RectsIndexFreeListStart; index_entry->Generation++; + if (index_entry->Generation == 0) + index_entry->Generation++; // Keep non-zero on overflow const int pack_padding = atlas->TexGlyphPadding; builder->RectsIndexFreeListStart = index_idx; @@ -4380,6 +4460,7 @@ ImTextureRect* ImFontAtlasPackGetRectSafe(ImFontAtlas* atlas, ImFontAtlasRectId if (atlas->Builder == NULL) ImFontAtlasBuildInit(atlas); ImFontAtlasBuilder* builder = (ImFontAtlasBuilder*)atlas->Builder; + IM_MSVC_WARNING_SUPPRESS(28182); // Static Analysis false positive "warning C28182: Dereferencing NULL pointer 'builder'" if (index_idx >= builder->RectsIndex.Size) return NULL; ImFontAtlasRectEntry* index_entry = &builder->RectsIndex[index_idx]; @@ -4425,14 +4506,14 @@ static ImWchar ImFontAtlas_FontHookRemapCodepoint(ImFontAtlas* atlas, ImFont* fo return (c == 0x00A0 || c == 0x202F) ? ImWchar(' ') : c; // NBSP/NNBSP } -static ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codepoint) +static ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codepoint, float* only_load_advance_x) { - ImFont* font = baked->ContainerFont; - ImFontAtlas* atlas = font->ContainerAtlas; + ImFont* font = baked->OwnerFont; + ImFontAtlas* atlas = font->OwnerAtlas; if (atlas->Locked || (font->Flags & ImFontFlags_NoLoadGlyphs)) { // Lazily load fallback glyph - if (baked->FallbackGlyphIndex == -1 && baked->LockLoadingFallback == 0) + if (baked->FallbackGlyphIndex == -1 && baked->LoadNoFallback == 0) ImFontAtlasBuildSetupFontBakedFallback(baked); return NULL; } @@ -4458,13 +4539,25 @@ static ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codep const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader; if (!src->GlyphExcludeRanges || ImFontAtlasBuildAcceptCodepointForSource(src, codepoint)) { - ImFontGlyph glyph_buf; - if (loader->FontBakedLoadGlyph(atlas, src, baked, loader_user_data_p, codepoint, &glyph_buf)) + if (only_load_advance_x == NULL) + { + ImFontGlyph glyph_buf; + if (loader->FontBakedLoadGlyph(atlas, src, baked, loader_user_data_p, codepoint, &glyph_buf, NULL)) + { + // FIXME: Add hooks for e.g. #7962 + glyph_buf.Codepoint = src_codepoint; + glyph_buf.SourceIdx = src_n; + return ImFontAtlasBakedAddFontGlyph(atlas, baked, src, &glyph_buf); + } + } + else { - // FIXME: Add hooks for e.g. #7962 - glyph_buf.Codepoint = src_codepoint; - glyph_buf.SourceIdx = src_n; - return ImFontAtlasBakedAddFontGlyph(atlas, baked, src, &glyph_buf); + // Special mode but only loading glyphs metrics. Will rasterize and pack later. + if (loader->FontBakedLoadGlyph(atlas, src, baked, loader_user_data_p, codepoint, NULL, only_load_advance_x)) + { + ImFontAtlasBakedAddFontGlyphAdvancedX(atlas, baked, src, codepoint, *only_load_advance_x); + return NULL; + } } } loader_user_data_p += loader->FontBakedSrcLoaderDataSize; @@ -4472,7 +4565,7 @@ static ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codep } // Lazily load fallback glyph - if (baked->LockLoadingFallback) + if (baked->LoadNoFallback) return NULL; if (baked->FallbackGlyphIndex == -1) ImFontAtlasBuildSetupFontBakedFallback(baked); @@ -4484,12 +4577,27 @@ static ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codep return NULL; } +static float ImFontBaked_BuildLoadGlyphAdvanceX(ImFontBaked* baked, ImWchar codepoint) +{ + if (baked->Size >= IMGUI_FONT_SIZE_THRESHOLD_FOR_LOADADVANCEXONLYMODE || baked->LoadNoRenderOnLayout) + { + // First load AdvanceX value used by CalcTextSize() API then load the rest when loaded by drawing API. + float only_advance_x = 0.0f; + ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(baked, (ImWchar)codepoint, &only_advance_x); + return glyph ? glyph->AdvanceX : only_advance_x; + } + else + { + ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(baked, (ImWchar)codepoint, NULL); + return glyph ? glyph->AdvanceX : baked->FallbackAdvanceX; + } +} + // The point of this indirection is to not be inlined in debug mode in order to not bloat inner loop.b IM_MSVC_RUNTIME_CHECKS_OFF static float BuildLoadGlyphGetAdvanceOrFallback(ImFontBaked* baked, unsigned int codepoint) { - ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(baked, (ImWchar)codepoint); - return glyph ? glyph->AdvanceX : baked->FallbackAdvanceX; + return ImFontBaked_BuildLoadGlyphAdvanceX(baked, (ImWchar)codepoint); } IM_MSVC_RUNTIME_CHECKS_RESTORE @@ -4506,16 +4614,16 @@ void ImFontAtlasDebugLogTextureRequests(ImFontAtlas* atlas) if (tex->Status == ImTextureStatus_WantCreate) IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: create %dx%d\n", tex->UniqueID, tex->Width, tex->Height); else if (tex->Status == ImTextureStatus_WantDestroy) - IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: destroy %dx%d, texid=0x%" IM_PRIX64 ", backend_data=%p\n", tex->UniqueID, tex->Width, tex->Height, tex->TexID, tex->BackendUserData); + IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: destroy %dx%d, texid=0x%" IM_PRIX64 ", backend_data=%p\n", tex->UniqueID, tex->Width, tex->Height, ImGui::DebugTextureIDToU64(tex->TexID), tex->BackendUserData); else if (tex->Status == ImTextureStatus_WantUpdates) { - IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: update %d regions, texid=0x%" IM_PRIX64 ", backend_data=0x%" IM_PRIX64 "\n", tex->UniqueID, tex->Updates.Size, tex->TexID, (ImU64)(intptr_t)tex->BackendUserData); + IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: update %d regions, texid=0x%" IM_PRIX64 ", backend_data=0x%" IM_PRIX64 "\n", tex->UniqueID, tex->Updates.Size, ImGui::DebugTextureIDToU64(tex->TexID), (ImU64)(intptr_t)tex->BackendUserData); for (const ImTextureRect& r : tex->Updates) { IM_UNUSED(r); IM_ASSERT(r.x >= 0 && r.y >= 0); IM_ASSERT(r.x + r.w <= tex->Width && r.y + r.h <= tex->Height); // In theory should subtract PackPadding but it's currently part of atlas and mid-frame change would wreck assert. - //IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: update (% 4d..%-4d)->(% 4d..%-4d), texid=0x%" IM_PRIX64 ", backend_data=0x%" IM_PRIX64 "\n", tex->UniqueID, r.x, r.y, r.x + r.w, r.y + r.h, tex->TexID, (ImU64)(intptr_t)tex->BackendUserData); + //IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: update (% 4d..%-4d)->(% 4d..%-4d), texid=0x%" IM_PRIX64 ", backend_data=0x%" IM_PRIX64 "\n", tex->UniqueID, r.x, r.y, r.x + r.w, r.y + r.h, ImGui::DebugTextureIDToU64(tex->TexID), (ImU64)(intptr_t)tex->BackendUserData); } } } @@ -4545,14 +4653,14 @@ static bool ImGui_ImplStbTrueType_FontSrcInit(ImFontAtlas* atlas, ImFontConfig* IM_ASSERT(src->FontLoaderData == NULL); // Initialize helper structure for font loading and verify that the TTF/OTF data is correct - const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)src->FontData, src->FontNo); + const int font_offset = stbtt_GetFontOffsetForIndex((const unsigned char*)src->FontData, src->FontNo); if (font_offset < 0) { IM_DELETE(bd_font_data); IM_ASSERT_USER_ERROR(0, "stbtt_GetFontOffsetForIndex(): FontData is incorrect, or FontNo cannot be found."); return false; } - if (!stbtt_InitFont(&bd_font_data->FontInfo, (unsigned char*)src->FontData, font_offset)) + if (!stbtt_InitFont(&bd_font_data->FontInfo, (const unsigned char*)src->FontData, font_offset)) { IM_DELETE(bd_font_data); IM_ASSERT_USER_ERROR(0, "stbtt_InitFont(): failed to parse FontData. It is correct and complete? Check FontDataSize."); @@ -4560,15 +4668,14 @@ static bool ImGui_ImplStbTrueType_FontSrcInit(ImFontAtlas* atlas, ImFontConfig* } src->FontLoaderData = bd_font_data; + const float ref_size = src->DstFont->Sources[0]->SizePixels; if (src->MergeMode && src->SizePixels == 0.0f) - src->SizePixels = src->DstFont->Sources[0]->SizePixels; + src->SizePixels = ref_size; - if (src->SizePixels >= 0.0f) - bd_font_data->ScaleFactor = stbtt_ScaleForPixelHeight(&bd_font_data->FontInfo, 1.0f); - else - bd_font_data->ScaleFactor = stbtt_ScaleForMappingEmToPixels(&bd_font_data->FontInfo, 1.0f); - if (src->MergeMode && src->SizePixels != 0.0f) - bd_font_data->ScaleFactor *= src->SizePixels / src->DstFont->Sources[0]->SizePixels; // FIXME-NEWATLAS: Should tidy up that a bit + bd_font_data->ScaleFactor = stbtt_ScaleForPixelHeight(&bd_font_data->FontInfo, 1.0f); + if (src->MergeMode && src->SizePixels != 0.0f && ref_size != 0.0f) + bd_font_data->ScaleFactor *= src->SizePixels / ref_size; // FIXME-NEWATLAS: Should tidy up that a bit + bd_font_data->ScaleFactor *= src->ExtraSizeScale; return true; } @@ -4610,7 +4717,7 @@ static bool ImGui_ImplStbTrueType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig return true; } -static bool ImGui_ImplStbTrueType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void*, ImWchar codepoint, ImFontGlyph* out_glyph) +static bool ImGui_ImplStbTrueType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void*, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x) { // Search for first font which has the glyph ImGui_ImplStbTrueType_FontSrcData* bd_font_data = (ImGui_ImplStbTrueType_FontSrcData*)src->FontLoaderData; @@ -4632,7 +4739,14 @@ static bool ImGui_ImplStbTrueType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontC int advance, lsb; stbtt_GetGlyphBitmapBoxSubpixel(&bd_font_data->FontInfo, glyph_index, scale_for_raster_x, scale_for_raster_y, 0, 0, &x0, &y0, &x1, &y1); stbtt_GetGlyphHMetrics(&bd_font_data->FontInfo, glyph_index, &advance, &lsb); - const bool is_visible = (x0 != x1 && y0 != y1); + + // Load metrics only mode + if (out_advance_x != NULL) + { + IM_ASSERT(out_glyph == NULL); + *out_advance_x = advance * scale_for_layout; + return true; + } // Prepare glyph out_glyph->Codepoint = codepoint; @@ -4640,6 +4754,7 @@ static bool ImGui_ImplStbTrueType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontC // Pack and retrieve position inside texture atlas // (generally based on stbtt_PackFontRangesRenderIntoRects) + const bool is_visible = (x0 != x1 && y0 != y1); if (is_visible) { const int w = (x1 - x0 + oversample_h - 1); @@ -4659,26 +4774,19 @@ static bool ImGui_ImplStbTrueType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontC builder->TempBuffer.resize(w * h * 1); unsigned char* bitmap_pixels = builder->TempBuffer.Data; memset(bitmap_pixels, 0, w * h * 1); - stbtt_MakeGlyphBitmapSubpixel(&bd_font_data->FontInfo, bitmap_pixels, r->w - oversample_h + 1, r->h - oversample_v + 1, w, - scale_for_raster_x, scale_for_raster_y, 0, 0, glyph_index); - // Oversampling + // Render with oversampling // (those functions conveniently assert if pixels are not cleared, which is another safety layer) - if (oversample_h > 1) - stbtt__h_prefilter(bitmap_pixels, r->w, r->h, r->w, oversample_h); - if (oversample_v > 1) - stbtt__v_prefilter(bitmap_pixels, r->w, r->h, r->w, oversample_v); + float sub_x, sub_y; + stbtt_MakeGlyphBitmapSubpixelPrefilter(&bd_font_data->FontInfo, bitmap_pixels, w, h, w, + scale_for_raster_x, scale_for_raster_y, 0, 0, oversample_h, oversample_v, &sub_x, &sub_y, glyph_index); - const float ref_size = baked->ContainerFont->Sources[0]->SizePixels; + const float ref_size = baked->OwnerFont->Sources[0]->SizePixels; const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f; - float font_off_x = (src->GlyphOffset.x * offsets_scale); - float font_off_y = (src->GlyphOffset.y * offsets_scale); - if (src->PixelSnapH) // Snap scaled offset. This is to mitigate backward compatibility issues for GlyphOffset, but a better design would be welcome. - font_off_x = IM_ROUND(font_off_x); - if (src->PixelSnapV) - font_off_y = IM_ROUND(font_off_y); - font_off_x += stbtt__oversample_shift(oversample_h); - font_off_y += stbtt__oversample_shift(oversample_v) + IM_ROUND(baked->Ascent); + float font_off_x = ImFloor(src->GlyphOffset.x * offsets_scale + 0.5f); // Snap scaled offset. + float font_off_y = ImFloor(src->GlyphOffset.y * offsets_scale + 0.5f); + font_off_x += sub_x; + font_off_y += sub_y + IM_ROUND(baked->Ascent); float recip_h = 1.0f / (oversample_h * rasterizer_density); float recip_v = 1.0f / (oversample_v * rasterizer_density); @@ -4848,11 +4956,11 @@ const ImWchar* ImFontAtlas::GetGlyphRangesChineseSimplifiedCommon() 0xFF00, 0xFFEF, // Half-width characters 0xFFFD, 0xFFFD // Invalid }; - static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00) * 2 + 1] = { 0 }; + static ImWchar full_ranges[IM_COUNTOF(base_ranges) + IM_COUNTOF(accumulative_offsets_from_0x4E00) * 2 + 1] = { 0 }; if (!full_ranges[0]) { memcpy(full_ranges, base_ranges, sizeof(base_ranges)); - UnpackAccumulativeOffsetsIntoRanges(0x4E00, accumulative_offsets_from_0x4E00, IM_ARRAYSIZE(accumulative_offsets_from_0x4E00), full_ranges + IM_ARRAYSIZE(base_ranges)); + UnpackAccumulativeOffsetsIntoRanges(0x4E00, accumulative_offsets_from_0x4E00, IM_COUNTOF(accumulative_offsets_from_0x4E00), full_ranges + IM_COUNTOF(base_ranges)); } return &full_ranges[0]; } @@ -4938,11 +5046,11 @@ const ImWchar* ImFontAtlas::GetGlyphRangesJapanese() 0xFF00, 0xFFEF, // Half-width characters 0xFFFD, 0xFFFD // Invalid }; - static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00)*2 + 1] = { 0 }; + static ImWchar full_ranges[IM_COUNTOF(base_ranges) + IM_COUNTOF(accumulative_offsets_from_0x4E00)*2 + 1] = { 0 }; if (!full_ranges[0]) { memcpy(full_ranges, base_ranges, sizeof(base_ranges)); - UnpackAccumulativeOffsetsIntoRanges(0x4E00, accumulative_offsets_from_0x4E00, IM_ARRAYSIZE(accumulative_offsets_from_0x4E00), full_ranges + IM_ARRAYSIZE(base_ranges)); + UnpackAccumulativeOffsetsIntoRanges(0x4E00, accumulative_offsets_from_0x4E00, IM_COUNTOF(accumulative_offsets_from_0x4E00), full_ranges + IM_COUNTOF(base_ranges)); } return &full_ranges[0]; } @@ -4996,7 +5104,9 @@ const ImWchar* ImFontAtlas::GetGlyphRangesVietnamese() void ImFontGlyphRangesBuilder::AddText(const char* text, const char* text_end) { - while (text_end ? (text < text_end) : *text) + if (text_end == NULL) + text_end = text + strlen(text); + while (text < text_end) { unsigned int c = 0; int c_len = ImTextCharFromUtf8(&c, text, text_end); @@ -5029,7 +5139,7 @@ void ImFontGlyphRangesBuilder::BuildRanges(ImVector* out_ranges) } //----------------------------------------------------------------------------- -// [SECTION] ImFont +// [SECTION] ImFontBaked, ImFont //----------------------------------------------------------------------------- ImFontBaked::ImFontBaked() @@ -5064,9 +5174,9 @@ ImFont::~ImFont() void ImFont::ClearOutputData() { - if (ImFontAtlas* atlas = ContainerAtlas) + if (ImFontAtlas* atlas = OwnerAtlas) ImFontAtlasFontDiscardBakes(atlas, this, 0); - FallbackChar = EllipsisChar = 0; + //FallbackChar = EllipsisChar = 0; memset(Used8kPagesMap, 0, sizeof(Used8kPagesMap)); LastBaked = NULL; } @@ -5109,7 +5219,7 @@ ImFontGlyph* ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked if (src != NULL) { // Clamp & recenter if needed - const float ref_size = baked->ContainerFont->Sources[0]->SizePixels; + const float ref_size = baked->OwnerFont->Sources[0]->SizePixels; const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f; float advance_x = ImClamp(glyph->AdvanceX, src->GlyphMinAdvanceX * offsets_scale, src->GlyphMaxAdvanceX * offsets_scale); if (advance_x != glyph->AdvanceX) @@ -5135,18 +5245,41 @@ ImFontGlyph* ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked baked->IndexAdvanceX[codepoint] = glyph->AdvanceX; baked->IndexLookup[codepoint] = (ImU16)glyph_idx; const int page_n = codepoint / 8192; - baked->ContainerFont->Used8kPagesMap[page_n >> 3] |= 1 << (page_n & 7); + baked->OwnerFont->Used8kPagesMap[page_n >> 3] |= 1 << (page_n & 7); return glyph; } +// FIXME: Code is duplicated with code above. +void ImFontAtlasBakedAddFontGlyphAdvancedX(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImWchar codepoint, float advance_x) +{ + IM_UNUSED(atlas); + if (src != NULL) + { + // Clamp & recenter if needed + const float ref_size = baked->OwnerFont->Sources[0]->SizePixels; + const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f; + advance_x = ImClamp(advance_x, src->GlyphMinAdvanceX * offsets_scale, src->GlyphMaxAdvanceX * offsets_scale); + + // Snap to pixel + if (src->PixelSnapH) + advance_x = IM_ROUND(advance_x); + + // Bake spacing + advance_x += src->GlyphExtraAdvanceX; + } + + ImFontBaked_BuildGrowIndex(baked, codepoint + 1); + baked->IndexAdvanceX[codepoint] = advance_x; +} + // Copy to texture, post-process and queue update for backend void ImFontAtlasBakedSetFontGlyphBitmap(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImFontGlyph* glyph, ImTextureRect* r, const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch) { ImTextureData* tex = atlas->TexData; IM_ASSERT(r->x + r->w <= tex->Width && r->y + r->h <= tex->Height); ImFontAtlasTextureBlockConvert(src_pixels, src_fmt, src_pitch, (unsigned char*)tex->GetPixelsAt(r->x, r->y), tex->Format, tex->GetPitch(), r->w, r->h); - ImFontAtlasPostProcessData pp_data = { atlas, baked->ContainerFont, src, baked, glyph, tex->GetPixelsAt(r->x, r->y), tex->Format, tex->GetPitch(), r->w, r->h }; + ImFontAtlasPostProcessData pp_data = { atlas, baked->OwnerFont, src, baked, glyph, tex->GetPixelsAt(r->x, r->y), tex->Format, tex->GetPitch(), r->w, r->h }; ImFontAtlasTextureBlockPostProcess(&pp_data); ImFontAtlasTextureBlockQueueUpload(atlas, tex, r->x, r->y, r->w, r->h); } @@ -5167,7 +5300,7 @@ ImFontGlyph* ImFontBaked::FindGlyph(ImWchar c) if (i != IM_FONTGLYPH_INDEX_UNUSED) return &Glyphs.Data[i]; } - ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c); + ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c, NULL); return glyph ? glyph : &Glyphs.Data[FallbackGlyphIndex]; } @@ -5182,9 +5315,9 @@ ImFontGlyph* ImFontBaked::FindGlyphNoFallback(ImWchar c) if (i != IM_FONTGLYPH_INDEX_UNUSED) return &Glyphs.Data[i]; } - LockLoadingFallback = true; // This is actually a rare call, not done in hot-loop, so we prioritize not adding extra cruft to ImFontBaked_BuildLoadGlyph() call sites. - ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c); - LockLoadingFallback = false; + LoadNoFallback = true; // This is actually a rare call, not done in hot-loop, so we prioritize not adding extra cruft to ImFontBaked_BuildLoadGlyph() call sites. + ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c, NULL); + LoadNoFallback = false; return glyph; } @@ -5204,7 +5337,7 @@ bool ImFontBaked::IsGlyphLoaded(ImWchar c) // This is not fast query bool ImFont::IsGlyphInFont(ImWchar c) { - ImFontAtlas* atlas = ContainerAtlas; + ImFontAtlas* atlas = OwnerAtlas; c = ImFontAtlas_FontHookRemapCodepoint(atlas, this, c); for (ImFontConfig* src : Sources) { @@ -5226,10 +5359,7 @@ float ImFontBaked::GetCharAdvance(ImWchar c) if (x >= 0.0f) return x; } - - // Same as BuildLoadGlyphGetAdvanceOrFallback(): - const ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c); - return glyph ? glyph->AdvanceX : FallbackAdvanceX; + return ImFontBaked_BuildLoadGlyphAdvanceX(this, c); } IM_MSVC_RUNTIME_CHECKS_RESTORE @@ -5258,7 +5388,7 @@ ImFontBaked* ImFont::GetFontBaked(float size, float font_weight, float density) if (baked && baked->Size == size && baked->Weight == font_weight && baked->RasterizerDensity == density) return baked; - ImFontAtlas* atlas = ContainerAtlas; + ImFontAtlas* atlas = OwnerAtlas; ImFontAtlasBuilder* builder = atlas->Builder; baked = ImFontAtlasBakedGetOrAdd(atlas, this, size, font_weight, density); if (baked == NULL) @@ -5279,7 +5409,7 @@ ImFontBaked* ImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas, ImFont* font, float fo ImFontBaked* baked = *p_baked_in_map; if (baked != NULL) { - IM_ASSERT(baked->Size == font_size && baked->Weight == font_weight && baked->ContainerFont == font && baked->BakedId == baked_id); + IM_ASSERT(baked->Size == font_size && baked->Weight == font_weight && baked->OwnerFont == font && baked->BakedId == baked_id); return baked; } @@ -5304,46 +5434,98 @@ ImFontBaked* ImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas, ImFont* font, float fo } // Trim trailing space and find beginning of next line -static inline const char* CalcWordWrapNextLineStartA(const char* text, const char* text_end) +const char* ImTextCalcWordWrapNextLineStart(const char* text, const char* text_end, ImDrawTextFlags flags) { - while (text < text_end && ImCharIsBlankA(*text)) - text++; - if (*text == '\n') + if ((flags & ImDrawTextFlags_WrapKeepBlanks) == 0) + while (text < text_end && ImCharIsBlankA(*text)) + text++; + if (text < text_end && *text == '\n') text++; return text; } +void ImTextClassifierClear(ImU32* bits, unsigned int codepoint_min, unsigned int codepoint_end, ImWcharClass char_class) +{ + for (unsigned int c = codepoint_min; c < codepoint_end; c++) + ImTextClassifierSetCharClass(bits, codepoint_min, codepoint_end, char_class, c); +} + +void ImTextClassifierSetCharClass(ImU32* bits, unsigned int codepoint_min, unsigned int codepoint_end, ImWcharClass char_class, unsigned int c) +{ + IM_ASSERT(c >= codepoint_min && c < codepoint_end); + IM_UNUSED(codepoint_end); + c -= codepoint_min; + const ImU32 shift = (c & 15) << 1; + bits[c >> 4] = (bits[c >> 4] & ~(0x03 << shift)) | (char_class << shift); +} + +void ImTextClassifierSetCharClassFromStr(ImU32* bits, unsigned int codepoint_min, unsigned int codepoint_end, ImWcharClass char_class, const char* s) +{ + const char* s_end = s + strlen(s); + while (*s) + { + unsigned int c; + s += ImTextCharFromUtf8(&c, s, s_end); + ImTextClassifierSetCharClass(bits, codepoint_min, codepoint_end, char_class, c); + } +} + +#define ImTextClassifierGet(_BITS, _CHAR_OFFSET) ((_BITS[(_CHAR_OFFSET) >> 4] >> (((_CHAR_OFFSET) & 15) << 1)) & 0x03) + +// 2-bit per character +static ImU32 g_CharClassifierIsSeparator_0000_007f[128 / 16] = {}; +static ImU32 g_CharClassifierIsSeparator_3000_300f[ 16 / 16] = {}; + +void ImTextInitClassifiers() +{ + if (ImTextClassifierGet(g_CharClassifierIsSeparator_0000_007f, ',') != 0) + return; + + // List of hardcoded separators: .,;!?'" + // Making this dynamic given known ranges is trivial BUT requires us to standardize where you pass them as parameters. (#3002, #8503) + ImTextClassifierClear(g_CharClassifierIsSeparator_0000_007f, 0, 128, ImWcharClass_Other); + ImTextClassifierSetCharClassFromStr(g_CharClassifierIsSeparator_0000_007f, 0, 128, ImWcharClass_Blank, " \t"); + ImTextClassifierSetCharClassFromStr(g_CharClassifierIsSeparator_0000_007f, 0, 128, ImWcharClass_Punct, ".,;!?\""); + + ImTextClassifierClear(g_CharClassifierIsSeparator_3000_300f, 0x3000, 0x300F, ImWcharClass_Other); + ImTextClassifierSetCharClass(g_CharClassifierIsSeparator_3000_300f, 0x3000, 0x300F, ImWcharClass_Blank, 0x3000); + ImTextClassifierSetCharClass(g_CharClassifierIsSeparator_3000_300f, 0x3000, 0x300F, ImWcharClass_Punct, 0x3001); + ImTextClassifierSetCharClass(g_CharClassifierIsSeparator_3000_300f, 0x3000, 0x300F, ImWcharClass_Punct, 0x3002); +} + // Simple word-wrapping for English, not full-featured. Please submit failing cases! // This will return the next location to wrap from. If no wrapping if necessary, this will fast-forward to e.g. text_end. -// FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.) -const char* ImFont::CalcWordWrapPosition(float size, float weight, const char* text, const char* text_end, float wrap_width) +// Refer to imgui_test_suite's "drawlist_text_wordwrap_1" for tests. +const char* ImFontCalcWordWrapPositionEx(ImFont* font, float size, float weight, const char* text, const char* text_end, float wrap_width, ImDrawTextFlags flags) { // For references, possible wrap point marked with ^ // "aaa bbb, ccc,ddd. eee fff. ggg!" // ^ ^ ^ ^ ^__ ^ ^ - // List of hardcoded separators: .,;!?'" - // Skip extra blanks after a line returns (that includes not counting them in width computation) // e.g. "Hello world" --> "Hello" "World" // Cut words that cannot possibly fit within one line. // e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish" - ImFontBaked* baked = GetFontBaked(size, weight); + ImFontBaked* baked = font->GetFontBaked(size, weight); const float scale = size / baked->Size; float line_width = 0.0f; - float word_width = 0.0f; float blank_width = 0.0f; wrap_width /= scale; // We work with unscaled widths to avoid scaling every characters - const char* word_end = text; - const char* prev_word_end = NULL; - bool inside_word = true; - const char* s = text; IM_ASSERT(text_end != NULL); + + int prev_type = ImWcharClass_Other; + const bool keep_blanks = (flags & ImDrawTextFlags_WrapKeepBlanks) != 0; + + // Find next wrapping point + //const char* span_begin = s; + const char* span_end = s; + float span_width = 0.0f; + while (s < text_end) { unsigned int c = (unsigned int)*s; @@ -5356,15 +5538,10 @@ const char* ImFont::CalcWordWrapPosition(float size, float weight, const char* t if (c < 32) { if (c == '\n') - { - line_width = word_width = blank_width = 0.0f; - inside_word = true; - s = next_s; - continue; - } + return s; // Direct return, skip "Wrap_width is too small to fit anything" path. if (c == '\r') { - s = next_s; + s = next_s; // Fast-skip continue; } } @@ -5374,44 +5551,56 @@ const char* ImFont::CalcWordWrapPosition(float size, float weight, const char* t if (char_width < 0.0f) char_width = BuildLoadGlyphGetAdvanceOrFallback(baked, c); - if (ImCharIsBlankW(c)) + // Classify current character + int curr_type; + if (c < 128) + curr_type = ImTextClassifierGet(g_CharClassifierIsSeparator_0000_007f, c); + else if (c >= 0x3000 && c < 0x3010) + curr_type = ImTextClassifierGet(g_CharClassifierIsSeparator_3000_300f, c & 15); //-V578 + else + curr_type = ImWcharClass_Other; + + if (curr_type == ImWcharClass_Blank) { - if (inside_word) + // End span: 'A ' or '. ' + if (prev_type != ImWcharClass_Blank && !keep_blanks) { - line_width += blank_width; - blank_width = 0.0f; - word_end = s; + span_end = s; + line_width += span_width; + span_width = 0.0f; } blank_width += char_width; - inside_word = false; } else { - word_width += char_width; - if (inside_word) + // End span: '.X' unless X is a digit + if (prev_type == ImWcharClass_Punct && curr_type != ImWcharClass_Punct && !(c >= '0' && c <= '9')) // FIXME: Digit checks might be removed if allow custom separators (#8503) { - word_end = next_s; + span_end = s; + line_width += span_width + blank_width; + span_width = blank_width = 0.0f; } - else + // End span: 'A ' or '. ' + else if (prev_type == ImWcharClass_Blank && keep_blanks) { - prev_word_end = word_end; - line_width += word_width + blank_width; - word_width = blank_width = 0.0f; + span_end = s; + line_width += span_width + blank_width; + span_width = blank_width = 0.0f; } - - // Allow wrapping after punctuation. - inside_word = (c != '.' && c != ',' && c != ';' && c != '!' && c != '?' && c != '\"' && c != 0x3001 && c != 0x3002); + span_width += char_width; } - // We ignore blank width at the end of the line (they can be skipped) - if (line_width + word_width > wrap_width) + if (span_width + blank_width + line_width > wrap_width) { - // Words that cannot possibly fit within an entire line will be cut anywhere. - if (word_width < wrap_width) - s = prev_word_end ? prev_word_end : word_end; - break; + if (span_width + blank_width > wrap_width) + break; + // FIXME: Narrow wrapping e.g. "A quick brown" -> "Quic|k br|own", would require knowing if span is going to be longer than wrap_width. + //if (span_width > wrap_width && !is_blank && !was_blank) + // return s; + return span_end; } + prev_type = curr_type; s = next_s; } @@ -5422,14 +5611,21 @@ const char* ImFont::CalcWordWrapPosition(float size, float weight, const char* t return s; } -ImVec2 ImFont::CalcTextSizeA(float size, float weight, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** remaining) +const char* ImFont::CalcWordWrapPosition(float size, float weight, const char* text, const char* text_end, float wrap_width) +{ + return ImFontCalcWordWrapPositionEx(this, size, weight, text, text_end, wrap_width, ImDrawTextFlags_None); +} + +ImVec2 ImFontCalcTextSizeEx(ImFont* font, float size, float weight, float max_width, float wrap_width, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining, ImVec2* out_offset, ImDrawTextFlags flags) { if (!text_end) text_end = text_begin + ImStrlen(text_begin); // FIXME-OPT: Need to avoid this. + if (!text_end_display) + text_end_display = text_end; + ImFontBaked* baked = font->GetFontBaked(size, weight); const float line_height = size; - ImFontBaked* baked = GetFontBaked(size, weight); - const float scale = size / baked->Size; + const float scale = line_height / baked->Size; ImVec2 text_size = ImVec2(0, 0); float line_width = 0.0f; @@ -5438,13 +5634,14 @@ ImVec2 ImFont::CalcTextSizeA(float size, float weight, float max_width, float wr const char* word_wrap_eol = NULL; const char* s = text_begin; - while (s < text_end) + while (s < text_end_display) { + // Word-wrapping if (word_wrap_enabled) { // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature. if (!word_wrap_eol) - word_wrap_eol = CalcWordWrapPosition(size, weight, s, text_end, wrap_width - line_width); + word_wrap_eol = ImFontCalcWordWrapPositionEx(font, size, weight, s, text_end, wrap_width - line_width, flags); if (s >= word_wrap_eol) { @@ -5452,8 +5649,10 @@ ImVec2 ImFont::CalcTextSizeA(float size, float weight, float max_width, float wr text_size.x = line_width; text_size.y += line_height; line_width = 0.0f; + s = ImTextCalcWordWrapNextLineStart(s, text_end, flags); // Wrapping skips upcoming blanks + if (flags & ImDrawTextFlags_StopOnNewLine) + break; word_wrap_eol = NULL; - s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks continue; } } @@ -5466,18 +5665,17 @@ ImVec2 ImFont::CalcTextSizeA(float size, float weight, float max_width, float wr else s += ImTextCharFromUtf8(&c, s, text_end); - if (c < 32) + if (c == '\n') { - if (c == '\n') - { - text_size.x = ImMax(text_size.x, line_width); - text_size.y += line_height; - line_width = 0.0f; - continue; - } - if (c == '\r') - continue; + text_size.x = ImMax(text_size.x, line_width); + text_size.y += line_height; + line_width = 0.0f; + if (flags & ImDrawTextFlags_StopOnNewLine) + break; + continue; } + if (c == '\r') + continue; // Optimized inline version of 'float char_width = GetCharAdvance((ImWchar)c);' float char_width = (c < (unsigned int)baked->IndexAdvanceX.Size) ? baked->IndexAdvanceX.Data[c] : -1.0f; @@ -5497,15 +5695,23 @@ ImVec2 ImFont::CalcTextSizeA(float size, float weight, float max_width, float wr if (text_size.x < line_width) text_size.x = line_width; - if (line_width > 0 || text_size.y == 0.0f) + if (out_offset != NULL) + *out_offset = ImVec2(line_width, text_size.y + line_height); // offset allow for the possibility of sitting after a trailing \n + + if (line_width > 0 || text_size.y == 0.0f) // whereas size.y will ignore the trailing \n text_size.y += line_height; - if (remaining) - *remaining = s; + if (out_remaining != NULL) + *out_remaining = s; return text_size; } +ImVec2 ImFont::CalcTextSizeA(float size, float weight, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** out_remaining) +{ + return ImFontCalcTextSizeEx(this, size, weight, max_width, wrap_width, text_begin, text_end, text_end, out_remaining, NULL, ImDrawTextFlags_None); +} + // Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound. void ImFont::RenderChar(ImDrawList* draw_list, float size, float weight, const ImVec2& pos, ImU32 col, ImWchar c, const ImVec4* cpu_fine_clip) { @@ -5546,7 +5752,8 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, float weight, const I } // Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound. -void ImFont::RenderText(ImDrawList* draw_list, float size, float weight, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) +// DO NOT CALL DIRECTLY THIS WILL CHANGE WILDLY IN 2026. Use ImDrawList::AddText(). +void ImFont::RenderText(ImDrawList* draw_list, float size, float weight, 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: @@ -5576,8 +5783,8 @@ begin: // FIXME-OPT: This is not optimal as do first do a search for \n before calling CalcWordWrapPosition(). // If the specs for CalcWordWrapPosition() were reworked to optionally return on \n we could combine both. // However it is still better than nothing performing the fast-forward! - s = CalcWordWrapPosition(size, weight, s, line_end ? line_end : text_end, wrap_width); - s = CalcWordWrapNextLineStartA(s, text_end); + s = ImFontCalcWordWrapPositionEx(this, size, weight, s, line_end ? line_end : text_end, wrap_width, flags); + s = ImTextCalcWordWrapNextLineStart(s, text_end, flags); } else { @@ -5612,6 +5819,7 @@ begin: ImDrawIdx* idx_write = draw_list->_IdxWritePtr; unsigned int vtx_index = draw_list->_VtxCurrentIdx; const int cmd_count = draw_list->CmdBuffer.Size; + const bool cpu_fine_clip = (flags & ImDrawTextFlags_CpuFineClip) != 0; const ImU32 col_untinted = col | ~IM_COL32_A_MASK; const char* word_wrap_eol = NULL; @@ -5622,7 +5830,7 @@ begin: { // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature. if (!word_wrap_eol) - word_wrap_eol = CalcWordWrapPosition(size, weight, s, text_end, wrap_width - (x - origin_x)); + word_wrap_eol = ImFontCalcWordWrapPositionEx(this, size, weight, s, text_end, wrap_width - (x - origin_x), flags); if (s >= word_wrap_eol) { @@ -5631,7 +5839,7 @@ begin: if (y > clip_rect.w) break; // break out of main loop word_wrap_eol = NULL; - s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks + s = ImTextCalcWordWrapNextLineStart(s, text_end, flags); // Wrapping skips upcoming blanks continue; } } @@ -5758,7 +5966,7 @@ begin: // - RenderBullet() // - RenderCheckMark() // - RenderArrowPointingAt() -// - RenderRectFilledRangeH() +// - RenderRectFilledInRangeH() // - RenderRectFilledWithHole() //----------------------------------------------------------------------------- // Function in need of a redesign (legacy mess) @@ -5799,8 +6007,9 @@ void ImGui::RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir d void ImGui::RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col) { - // FIXME-OPT: This should be baked in font. - draw_list->AddCircleFilled(pos, draw_list->_Data->FontSize * 0.20f, col, 8); + // FIXME-OPT: This should be baked in font now that it's easier. + float font_size = draw_list->_Data->FontSize; + draw_list->AddCircleFilled(pos, font_size * 0.20f, col, (font_size < 22) ? 8 : (font_size < 40) ? 12 : 0); // Hardcode optimal/nice tessellation threshold } void ImGui::RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz) @@ -5840,15 +6049,15 @@ static inline float ImAcos01(float x) } // FIXME: Cleanup and move code to ImDrawList. -void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding) +// - Before 2025-12-04: RenderRectFilledRangeH() with 'float x_start_norm, float x_end_norm` <- normalized +// - After 2025-12-04: RenderRectFilledInRangeH() with 'float x1, float x2' <- absolute coords!! +void ImGui::RenderRectFilledInRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float fill_x0, float fill_x1, float rounding) { - if (x_end_norm == x_start_norm) + if (fill_x0 > fill_x1) return; - if (x_start_norm > x_end_norm) - ImSwap(x_start_norm, x_end_norm); - ImVec2 p0 = ImVec2(ImLerp(rect.Min.x, rect.Max.x, x_start_norm), rect.Min.y); - ImVec2 p1 = ImVec2(ImLerp(rect.Min.x, rect.Max.x, x_end_norm), rect.Max.y); + ImVec2 p0 = ImVec2(fill_x0, rect.Min.y); + ImVec2 p1 = ImVec2(fill_x1, rect.Max.y); if (rounding == 0.0f) { draw_list->AddRectFilled(p0, p1, col, 0.0f); @@ -6083,6 +6292,7 @@ static unsigned int stb_decompress(unsigned char *output, const unsigned char *i // Copyright (c) 2004, 2005 Tristan Grimmer // MIT license (see License.txt in http://www.proggyfonts.net/index.php?menu=download) // Download and more information at http://www.proggyfonts.net or http://upperboundsinteractive.com/fonts.php +// If you want a similar font which may be better scaled, consider using ProggyVector from the same author! //----------------------------------------------------------------------------- #ifndef IMGUI_DISABLE_DEFAULT_FONT @@ -6260,11 +6470,340 @@ 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* GetDefaultCompressedFontDataTTF(int* out_size) +static const char* GetDefaultCompressedFontDataProggyClean(int* out_size) { *out_size = proggy_clean_ttf_compressed_size; return (const char*)proggy_clean_ttf_compressed_data; } + +//----------------------------------------------------------------------------- +// [SECTION] Default font data (ProggyVector-minimal.ttf) +//----------------------------------------------------------------------------- +// Based on ProggyVector.ttf, Copyright (c) 2019,2023 Tristan Grimmer / Copyright (c) 2018 Source Foundry Authors / Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. +// MIT license + public domain, see licenses at https://github.com/bluescan/proggyfonts/tree/master/ProggyVector +//----------------------------------------------------------------------------- +// See misc/fonts/ProggyVector-ExtractScript.txt for FontForge script to create this from ProggyVector.ttf +//----------------------------------------------------------------------------- + +// File: 'ProggyVector-minimal.ttf' (23952 bytes) +// Exported using binary_to_compressed_c.exe -u8 "ProggyVector-minimal.ttf" proggy_vector_minimal_ttf +static const unsigned int proggy_vector_minimal_ttf_compressed_size = 18519; +static const unsigned char proggy_vector_minimal_ttf_compressed_data[18519] = +{ + 87,188,0,0,0,0,0,0,0,0,93,144,0,4,0,0,55,0,1,0,0,0,14,0,128,0,3,0,96,70,70,84,77,165,245,48,76,0,0,93,116,130,21,38,28,71,68,69,70,0,37,130,9,34,0,93,92,130,5,44,24,79,83,47,50,135, + 98,111,82,0,0,1,104,130,15,44,96,99,109,97,112,10,236,8,177,0,0,3,88,130,19,44,82,99,118,116,32,0,34,2,136,0,0,4,172,130,31,38,4,103,97,115,112,255,255,130,89,34,0,93,84,130,15,56, + 8,103,108,121,102,169,208,238,39,0,0,6,56,0,0,84,32,104,101,97,100,39,214,168,186,130,27,32,236,130,3,33,54,104,130,16,35,6,225,0,231,130,75,32,36,130,15,40,36,104,109,116,120,37,72, + 32,85,130,15,32,200,130,3,40,142,108,111,99,97,26,185,47,212,130,95,32,176,130,15,40,136,109,97,120,112,0,201,0,151,130,11,32,72,130,47,43,32,110,97,109,101,10,160,159,163,0,0,90,131, + 143,44,68,112,111,115,116,153,185,200,234,0,0,91,156,130,35,32,184,132,235,45,1,0,0,96,193,239,192,95,15,60,245,0,11,4,130,54,37,0,0,225,81,71,241,131,8,43,228,205,29,41,255,247,255, + 2,2,162,3,161,130,15,34,8,0,2,130,5,131,2,32,1,130,225,38,182,254,184,0,143,2,102,130,31,34,196,2,162,132,73,131,25,135,3,32,4,132,17,37,195,0,104,0,5,0,134,49,131,6,132,3,32,46,132, + 5,36,2,2,102,1,144,131,29,39,2,138,2,188,0,47,0,140,131,7,40,255,92,1,224,0,49,1,2,0,131,0,32,9,132,57,143,61,43,0,66,105,114,100,0,64,0,32,32,172,3,132,131,47,3,182,1,72,32,63,0,255, + 223,253,0,0,2,14,3,182,130,48,38,32,0,1,2,102,0,34,130,9,32,0,130,7,132,3,35,0,237,0,169,130,147,46,91,0,19,0,29,1,10,0,168,0,205,0,46,0,41,130,5,32,83,130,25,44,65,0,67,0,132,0,76, + 0,75,0,40,0,72,130,11,40,70,0,66,0,63,0,239,0,209,130,33,52,45,0,44,0,81,255,251,0,18,0,79,0,70,0,69,0,94,0,117,0,51,130,7,34,100,0,61,130,5,34,108,0,38,130,21,36,58,0,86,0,58,130, + 57,42,70,0,24,0,60,0,29,0,2,0,9,130,47,50,55,0,148,0,62,0,154,0,61,255,247,0,167,0,68,0,96,0,82,130,51,34,62,0,66,130,105,40,97,0,166,0,118,0,113,0,141,130,97,32,97,130,71,32,95,130, + 3,40,129,0,106,0,128,0,84,0,50,130,183,32,38,130,85,38,101,0,92,1,6,0,102,130,147,36,0,0,232,0,105,130,17,34,102,0,18,130,187,34,99,0,160,130,33,38,135,0,40,0,44,0,178,130,9,34,159, + 0,150,130,9,52,161,0,163,0,238,0,83,0,53,0,227,0,198,0,172,0,122,0,116,0,13,132,1,32,96,130,139,32,18,136,3,32,0,130,71,33,98,0,133,1,33,100,0,133,1,32,4,132,195,32,58,130,195,131, + 3,36,75,0,4,0,74,134,1,32,18,130,33,32,94,130,187,32,68,136,3,32,20,130,197,32,62,130,215,131,3,34,109,0,134,132,1,33,69,0,131,195,32,69,130,195,131,3,34,44,0,24,130,15,32,97,132,3, + 36,52,0,95,0,52,130,123,130,166,33,0,3,130,4,131,3,34,28,0,1,130,9,36,0,0,76,0,3,132,9,32,28,130,111,32,48,130,17,46,8,0,8,0,2,0,0,0,126,0,255,32,172,255,255,130,9,34,32,0,160,131, + 9,37,255,227,255,194,224,22,132,43,130,20,131,2,34,1,6,0,136,14,35,1,2,0,0,132,55,130,6,136,2,8,152,1,0,0,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29, + 30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89, + 90,91,92,93,94,95,96,97,0,134,135,137,139,147,152,158,163,162,164,166,165,167,169,171,170,172,173,175,174,176,177,179,181,180,182,184,183,188,187,189,190,0,114,100,101,105,0,120,161, + 112,107,0,118,106,0,136,154,0,115,0,0,103,119,132,161,39,108,124,0,168,186,129,99,110,131,12,38,109,125,0,98,130,133,151,131,10,131,3,37,185,0,193,0,0,194,131,9,33,0,121,130,5,47,132, + 140,131,141,138,143,144,145,142,149,150,0,148,156,157,155,130,18,32,113,130,3,32,122,130,3,130,2,34,34,2,136,130,5,33,42,0,133,1,8,186,80,0,100,0,170,1,22,1,132,2,2,2,16,2,36,2,56, + 2,104,2,124,2,166,2,182,2,208,2,226,3,26,3,52,3,132,3,214,3,246,4,46,4,126,4,150,4,238,5,66,5,120,5,194,5,224,5,244,6,14,6,84,6,224,7,12,7,80,7,122,7,162,7,184,7,208,8,0,8,24,8,46, + 8,76,8,116,8,132,8,170,8,202,8,254,9,42,9,98,9,160,9,248,10,10,10,66,10,98,10,152,10,208,10,246,11,20,11,38,11,68,11,86,11,116,11,128,11,152,11,234,12,52,12,100,12,180,12,238,13,16, + 13,108,13,148,13,194,13,236,14,18,14,38,14,96,14,128,14,176,14,222,15,18,15,60,15,122,15,152,15,190,15,224,16,18,16,76,16,146,16,178,16,240,16,254,17,68,17,122,130,1,8,225,158,17,228, + 18,12,18,88,18,142,18,162,19,32,19,66,19,176,19,246,20,40,20,56,20,68,20,192,20,206,21,8,21,34,21,80,21,160,21,184,21,232,22,4,22,32,22,62,22,88,22,146,22,198,23,4,23,80,23,196,24, + 40,24,96,24,146,24,208,25,46,25,116,25,188,25,228,26,48,26,84,26,114,26,156,26,212,26,244,27,16,27,52,27,100,27,154,27,244,28,46,28,98,28,162,29,10,29,82,29,144,30,0,30,68,30,134,30, + 210,31,38,31,84,31,122,31,212,32,68,32,176,33,36,33,182,34,50,34,188,35,44,35,128,35,206,36,24,36,104,36,192,36,238,37,20,37,64,37,118,37,216,38,38,38,104,38,160,38,222,39,60,39,134, + 39,174,39,250,40,50,40,100,40,158,40,226,41,42,41,92,41,178,42,16,0,2,0,34,0,0,1,50,2,170,0,3,0,7,0,46,177,1,0,47,60,178,7,4,0,237,50,177,6,5,220,60,178,3,2,130,10,34,0,177,3,131,22, + 32,5,131,22,39,178,7,6,1,252,60,178,1,131,23,53,51,17,33,17,39,51,17,35,34,1,16,238,204,204,2,170,253,86,34,2,102,0,130,83,8,71,237,255,255,1,126,2,228,0,14,0,20,0,0,37,50,23,22,20, + 14,1,35,34,39,38,53,52,55,54,19,3,35,3,53,51,1,53,50,17,6,33,25,15,32,19,21,21,19,85,15,80,18,113,140,42,13,47,30,9,19,19,24,41,19,19,1,221,254,160,1,96,123,0,131,75,38,169,1,185,1, + 191,2,189,132,159,34,0,1,17,131,115,39,51,17,1,104,87,254,234,87,130,25,35,4,254,252,1,130,3,61,0,2,0,1,0,5,2,102,2,183,0,5,0,46,0,0,19,51,54,55,35,6,15,1,35,62,1,55,35,53,130,12,130, + 14,38,53,51,55,51,14,1,7,134,5,33,21,35,131,11,34,21,35,7,131,34,51,229,123,31,11,123,11,50,52,80,10,32,10,129,149,7,13,14,7,133,152,132,12,32,123,132,5,40,121,140,8,26,8,125,144,53, + 79,130,30,51,1,10,112,46,46,178,195,37,121,37,71,30,49,49,29,73,194,37,120,37,131,3,36,73,29,98,30,71,131,22,32,0,130,169,8,40,91,255,237,2,41,2,210,0,8,0,18,0,72,0,0,1,21,54,55,54, + 53,52,39,38,39,53,6,7,6,29,1,6,23,22,19,46,1,39,38,35,38,131,18,39,22,23,30,1,23,22,23,53,130,12,8,166,52,55,54,55,53,51,21,20,30,4,23,21,39,38,47,2,34,39,21,22,23,22,21,20,7,6,7,21, + 1,102,56,26,31,31,32,132,62,25,8,2,26,30,44,1,1,1,3,7,12,25,79,59,21,30,13,23,16,36,46,82,53,50,53,61,71,82,56,19,21,20,20,11,22,35,25,33,16,8,8,98,43,54,58,63,74,1,54,205,10,20,37, + 49,39,29,21,91,194,5,62,20,6,10,25,34,24,254,84,10,37,11,1,1,5,13,23,85,12,15,5,9,6,13,2,217,11,45,42,144,45,50,6,57,57,3,10,3,5,4,6,3,81,10,14,7,8,2,1,202,16,43,46,71,72,50,49,4,58, + 0,5,0,19,0,17,2,84,2,172,130,9,38,22,0,38,0,55,0,71,130,219,37,0,7,35,18,19,3,130,166,33,51,50,133,228,38,35,34,7,6,20,23,6,65,239,7,33,51,50,131,193,33,1,22,131,30,32,52,130,239,40, + 38,34,14,2,21,20,55,20,7,140,33,8,124,2,40,254,177,58,96,234,160,60,13,34,7,11,24,20,20,20,19,21,52,17,5,165,43,54,52,46,38,39,36,61,55,41,40,254,24,13,34,27,20,20,5,10,27,12,27,23, + 20,10,204,68,33,25,67,41,39,67,30,26,68,42,40,2,172,253,200,98,1,140,1,14,253,205,28,10,1,18,20,19,32,18,19,38,11,27,77,38,37,38,53,57,36,36,37,40,51,54,1,162,18,18,20,38,11,23,11, + 4,8,18,22,14,27,28,75,37,17,37,35,56,73,40,16,38,36,0,130,0,63,3,0,29,255,243,2,98,2,209,0,11,0,37,0,85,0,0,19,62,1,53,52,35,34,6,7,6,23,22,3,20,23,132,223,45,63,1,54,55,62,2,55,38, + 39,38,47,1,38,39,130,26,43,55,38,53,54,55,54,51,50,30,1,29,1,130,208,41,7,30,3,23,54,61,1,54,38,59,132,13,34,22,23,34,131,227,37,6,43,1,34,39,38,130,22,8,147,237,30,57,71,34,40,3,14, + 38,19,102,53,29,57,18,15,53,41,12,5,6,7,4,3,2,26,20,34,19,67,17,27,47,26,22,50,86,2,75,42,52,35,78,35,34,24,59,33,53,40,46,26,38,1,2,1,81,20,19,36,36,49,43,47,5,10,40,81,104,8,100, + 73,70,13,1,221,18,49,44,61,35,14,53,35,17,254,239,71,49,27,13,4,23,6,3,3,4,5,1,1,34,26,41,26,86,21,36,36,33,38,163,66,66,99,37,23,23,60,24,38,41,45,31,41,42,69,53,57,33,46,91,26,6, + 24,108,52,51,38,45,63,1,52,64,63,64,85,20,127,130,250,52,0,1,1,10,1,186,1,102,2,191,0,3,0,0,1,17,51,17,1,10,92,130,16,34,5,254,251,130,14,41,0,168,255,174,1,141,3,7,0,7,130,12,55,6, + 16,23,35,38,16,55,1,141,132,132,80,149,149,3,7,217,254,88,216,224,1,157,220,130,26,39,0,205,255,179,1,178,3,12,132,39,8,48,22,16,7,35,54,16,39,1,29,149,149,80,132,132,3,12,220,254, + 96,221,216,1,175,210,0,0,1,0,46,0,90,2,57,2,100,0,27,0,0,19,39,54,55,22,23,53,51,21,55,131,7,8,64,7,23,22,23,7,46,1,39,21,35,53,7,39,54,219,173,19,25,129,43,87,88,55,33,14,30,176,88, + 54,33,44,33,109,33,87,174,42,99,1,100,96,28,40,70,24,186,186,48,30,17,20,49,96,48,30,19,68,18,59,18,196,196,92,67,56,132,203,48,0,41,0,100,2,61,2,85,0,11,0,0,37,53,35,53,51,130,94, + 49,51,21,35,21,1,8,223,223,84,225,225,100,208,80,209,209,80,208,130,135,44,205,255,85,1,125,0,141,0,25,0,0,23,38,130,136,45,54,55,34,38,39,38,53,52,55,54,51,50,23,22,65,207,5,8,44, + 6,241,6,30,59,17,7,7,20,29,5,4,43,13,15,32,19,22,3,13,91,17,171,8,42,51,37,15,18,29,13,13,15,49,18,4,19,20,31,6,15,17,92,86,16,130,83,40,83,1,52,2,23,1,133,0,5,130,219,46,53,33,21, + 22,21,83,1,195,1,1,52,81,40,25,16,130,156,38,1,0,237,255,255,1,126,130,115,35,13,0,0,4,139,109,8,34,20,1,85,47,25,11,21,44,13,16,30,20,22,1,9,10,19,33,48,18,5,20,20,63,0,0,1,0,65,255, + 208,2,41,2,230,131,83,8,37,23,0,55,51,0,7,65,1,38,99,95,254,217,98,48,2,81,197,253,176,198,0,0,3,0,67,255,243,2,38,2,203,0,7,0,18,0,32,130,247,51,50,17,52,39,6,7,22,19,38,35,34,7,6, + 21,20,23,54,55,5,16,67,111,6,32,16,131,221,8,57,1,56,148,8,104,149,35,185,33,78,93,35,21,8,63,63,1,1,137,45,60,120,60,61,241,121,60,61,72,1,34,48,46,130,182,72,1,232,72,111,77,102, + 49,46,79,78,54,254,240,68,23,92,91,180,1,109,92,88,130,198,130,147,39,132,0,0,1,254,2,189,0,130,199,8,34,51,53,51,17,6,7,38,53,54,55,51,17,51,21,132,144,32,111,1,107,35,94,142,82,2, + 0,25,89,82,33,80,26,253,149,82,133,51,44,76,0,0,2,30,2,202,0,54,0,0,1,34,130,48,33,62,1,131,137,36,21,20,14,1,7,130,2,49,6,15,1,14,5,7,33,21,33,53,52,62,4,63,1,62,3,55,69,50,5,8,102, + 46,1,35,1,27,70,132,1,96,93,23,100,67,65,30,12,8,20,49,10,30,34,18,9,14,20,19,20,28,17,1,100,254,46,27,33,39,42,40,12,22,17,14,9,18,11,47,9,5,37,16,53,15,2,123,73,58,29,51,14,55,52, + 89,49,62,15,12,30,49,11,33,34,16,7,18,16,18,22,27,17,82,63,17,26,34,38,43,40,13,22,19,12,12,20,13,61,40,19,24,52,33,15,19,130,211,44,75,255,243,2,34,2,203,0,56,0,0,19,54,131,154,69, + 120,6,69,128,6,37,43,1,38,39,46,1,69,175,5,33,22,51,68,234,6,34,43,1,53,68,245,11,8,161,7,97,120,73,74,64,58,26,16,6,31,97,93,37,22,69,67,127,25,12,14,54,35,14,23,31,65,23,62,45,131, + 34,10,44,44,67,86,77,107,30,10,36,33,72,51,88,25,26,2,172,31,29,26,56,34,29,46,17,78,24,23,68,36,51,101,55,55,1,1,4,10,3,5,11,95,30,5,15,77,25,32,65,34,36,78,63,20,27,54,28,29,21,7, + 10,0,2,0,40,0,0,2,64,2,189,0,6,0,17,0,0,55,51,17,14,3,39,1,51,17,51,21,35,21,35,53,33,136,242,61,108,39,24,106,1,26,135,119,119,90,254,185,245,1,114,87,170,61,38,6,1,180,254,56,78, + 167,167,0,0,0,1,0,72,130,227,32,23,130,63,32,36,135,227,36,21,20,7,14,1,132,218,34,2,39,53,136,214,8,69,39,38,34,7,17,33,21,33,196,47,35,120,69,68,109,50,80,22,36,12,14,69,41,20,10, + 88,96,129,37,11,9,15,17,53,170,66,1,135,254,214,1,194,14,65,64,109,141,62,25,11,1,1,5,12,7,3,97,46,90,30,29,48,22,36,15,47,35,1,96,79,130,175,38,67,255,246,2,38,2,191,130,173,32,54, + 130,175,136,95,66,149,6,38,22,39,54,51,50,23,22,70,9,6,67,123,7,39,54,59,1,30,2,23,21,38,130,149,8,91,35,6,7,6,214,34,68,101,25,9,53,30,51,69,34,37,21,8,46,51,117,105,57,60,61,56,117, + 130,58,49,9,3,89,89,104,9,19,7,25,32,30,17,8,60,44,19,9,9,73,69,51,108,43,92,30,39,99,42,22,43,46,75,59,36,12,249,97,63,62,226,63,60,85,71,113,38,43,192,83,87,1,4,9,4,3,88,32,1,2,71, + 71,0,130,0,8,62,1,0,70,0,0,2,28,2,189,0,9,0,0,9,1,35,18,55,54,55,33,53,33,2,28,254,244,105,120,101,22,17,254,155,1,214,2,150,253,106,1,39,232,54,41,79,0,0,3,0,66,255,243,2,39,2,203, + 0,14,0,28,0,58,130,209,33,20,23,132,211,34,52,39,38,131,210,34,55,50,53,134,9,32,21,130,24,34,37,20,7,66,58,6,36,35,34,38,53,52,130,91,33,38,39,68,96,6,130,244,8,95,167,82,26,33,65, + 40,37,37,37,68,69,35,37,141,125,32,30,63,93,24,8,72,23,1,0,121,78,29,31,16,58,170,113,128,39,24,75,43,23,32,9,14,60,61,208,61,61,198,100,28,10,36,37,128,37,35,35,35,145,115,60,28,30, + 67,22,28,85,24,7,126,135,26,17,46,46,75,49,38,115,104,97,73,49,47,16,11,26,32,25,38,35,76,50,49,49,51,130,222,39,0,2,0,63,255,249,2,35,130,175,38,13,0,53,0,0,19,20,66,171,11,40,18, + 38,39,53,22,23,30,1,54,130,152,36,6,7,14,1,34,133,156,130,14,66,251,10,8,97,6,7,38,163,134,68,34,37,79,29,23,78,30,34,59,32,72,36,31,19,36,61,35,100,18,18,19,39,63,81,40,86,30,15,23, + 11,61,139,130,59,34,12,15,23,42,93,47,63,36,1,219,164,43,43,78,118,36,11,41,42,253,206,1,17,78,15,6,1,2,1,18,50,198,36,15,30,16,15,33,90,44,42,71,67,19,97,89,49,65,73,62,114,65,133, + 46,22,4,2,133,167,45,239,0,111,1,129,2,66,0,15,0,32,0,0,1,72,26,14,34,3,22,21,130,17,132,159,32,52,130,172,40,54,50,1,129,22,20,23,39,20,130,0,57,23,55,21,5,45,44,22,19,48,13,26,11, + 6,5,10,29,13,31,1,251,33,18,20,20,16,35,130,5,49,42,13,254,236,17,56,24,20,19,5,9,28,13,31,13,25,12,5,130,108,36,2,0,209,255,197,132,107,38,31,0,47,0,0,23,54,130,81,35,34,38,39,38, + 132,89,69,248,10,37,7,6,7,46,3,19,142,137,60,209,42,14,18,15,19,29,5,4,5,9,29,13,15,31,20,22,3,3,4,21,29,51,29,6,9,8,8,171,139,147,62,9,36,19,24,42,29,13,13,31,12,26,12,5,20,20,31, + 6,12,18,29,12,54,34,58,18,7,13,11,12,2,11,138,162,130,146,8,57,0,1,0,41,0,116,2,57,2,75,0,14,0,0,45,1,53,36,55,21,14,2,7,30,2,31,1,2,57,253,240,1,140,132,91,126,156,52,28,74,115,78, + 130,116,196,78,148,49,86,33,44,55,19,9,26,41,27,45,132,207,38,45,0,205,2,61,1,245,75,31,5,36,55,53,33,21,37,130,3,44,45,2,16,253,240,2,16,205,81,81,216,80,80,133,99,58,44,0,114,2,60, + 2,73,0,11,0,0,1,37,53,4,23,21,5,53,62,1,55,54,1,213,254,87,130,98,60,253,240,28,73,58,195,1,93,151,85,148,49,78,196,86,9,26,21,68,0,2,0,81,255,255,2,33,2,223,130,151,130,107,75,199, + 14,61,3,39,62,1,51,50,22,29,1,6,15,1,14,1,29,1,35,53,54,55,62,2,53,54,39,38,43,1,34,6,75,223,12,56,124,72,33,130,78,106,117,2,85,32,29,44,96,2,87,17,48,45,1,25,38,73,9,37,94,75,243, + 12,56,156,46,52,85,86,73,4,77,60,22,21,44,30,55,54,72,61,13,34,45,25,41,20,29,57,131,191,44,2,255,251,255,225,2,162,2,224,0,16,0,95,130,193,36,35,38,7,6,7,74,116,10,46,19,22,23,14, + 1,7,6,35,34,39,38,39,46,1,53,65,206,8,33,21,20,130,39,42,6,35,17,38,35,53,6,7,6,46,1,68,101,6,8,203,54,30,1,31,1,22,23,51,22,55,54,61,1,54,39,46,2,34,6,7,14,2,29,1,20,23,22,23,62,1, + 1,111,10,10,17,53,6,6,22,32,37,38,29,29,62,17,236,3,50,12,49,40,78,96,83,79,104,75,45,18,27,56,132,64,54,196,88,31,8,14,39,14,19,56,18,20,38,30,69,46,33,53,83,43,60,26,43,17,7,14,9, + 12,34,12,11,5,2,38,18,71,53,42,37,32,84,77,27,82,82,122,81,117,1,229,1,8,27,94,64,32,32,35,33,60,91,29,8,254,167,3,59,10,43,19,37,25,32,109,63,104,50,98,64,142,54,26,98,37,60,18,26, + 47,15,7,254,242,1,45,34,16,14,6,27,29,62,86,119,66,37,2,22,10,4,14,9,13,2,23,10,6,7,25,36,16,33,7,2,11,31,113,93,47,12,115,100,87,3,1,41,0,0,2,0,18,130,4,45,86,2,189,0,10,0,23,0,0, + 19,51,46,3,39,130,202,8,61,1,15,1,35,54,19,51,22,18,30,1,23,35,39,202,213,13,39,30,18,7,13,19,37,30,32,54,105,41,188,123,28,83,63,39,15,104,56,1,3,44,132,98,62,23,43,67,125,100,100, + 183,124,2,65,85,254,254,192,120,46,183,0,130,0,38,3,0,79,255,255,2,58,132,87,45,21,0,44,0,0,55,51,50,55,54,52,38,39,38,70,243,10,42,43,1,3,17,51,50,23,22,23,20,7,71,197,5,68,240,7, + 8,88,175,149,124,26,10,20,20,40,80,149,127,104,23,8,33,32,82,115,96,207,112,64,60,2,6,13,21,6,12,30,17,84,26,41,66,69,129,78,67,24,67,51,16,32,78,60,21,28,57,21,24,253,144,2,189,46, + 46,86,39,15,30,26,7,13,10,7,22,38,47,76,94,50,49,0,1,0,70,255,243,2,25,2,203,0,25,0,0,37,6,76,84,10,39,21,38,35,34,7,6,21,16,131,151,8,41,55,2,25,76,90,147,76,78,139,71,91,88,78,79, + 87,98,49,49,197,58,68,20,19,25,38,95,96,171,232,90,44,39,97,58,71,74,142,254,225,34,10,15,130,218,39,0,2,0,69,0,0,2,44,130,219,34,12,0,23,134,217,34,53,52,39,132,219,133,208,8,32,21, + 16,7,6,35,170,86,153,32,13,50,25,23,41,62,83,101,189,172,63,63,172,55,71,78,142,54,78,157,56,30,11,18,131,173,39,85,87,179,254,250,66,22,0,130,163,32,94,130,79,32,35,130,79,38,11,0, + 0,51,17,33,21,135,1,49,94,1,196,254,161,1,53,254,203,1,96,2,189,79,208,80,254,80,130,43,32,117,130,43,36,41,2,190,0,12,131,43,34,50,54,51,131,44,53,51,21,35,17,117,28,75,60,1,17,254, + 174,246,246,2,189,1,76,222,76,254,184,131,171,34,1,0,51,130,255,32,40,130,255,32,31,135,255,74,164,8,32,21,130,177,65,3,8,8,53,53,35,53,51,2,40,86,116,71,56,61,36,75,80,81,139,96,79, + 25,38,43,58,109,47,49,194,60,45,134,230,57,70,24,29,45,94,170,175,94,97,50,98,25,19,26,71,71,146,254,226,30,189,78,0,130,143,32,69,130,143,32,36,135,187,79,93,5,55,35,17,33,17,69,101, + 1,20,102,102,254,236,2,189,254,225,1,31,253,67,1,78,254,178,130,144,34,1,0,100,130,47,32,3,134,47,37,53,51,17,35,53,33,130,188,47,51,21,100,157,157,1,159,157,157,80,2,30,79,79,253, + 226,68,115,6,36,61,255,247,1,222,130,43,32,17,131,187,33,7,6,66,58,7,131,52,61,17,20,1,169,38,51,29,63,183,185,80,23,28,191,1,36,41,38,7,5,77,30,36,95,1,137,79,254,40,132,134,151,32, + 100,130,59,53,24,0,0,51,17,51,17,62,3,55,51,6,7,30,2,23,35,46,3,39,6,7,130,159,51,38,116,86,55,20,119,194,97,126,102,52,19,122,29,90,66,42,16,58,19,130,173,52,200,38,115,85,54,20,191, + 95,173,143,72,27,43,129,96,60,23,57,20,254,238,130,79,32,108,130,183,32,57,130,79,32,5,133,79,41,33,21,108,101,1,104,2,189,253,147,134,171,32,38,130,31,32,68,130,31,50,20,0,0,1,3,35, + 39,46,2,39,17,35,17,51,22,23,62,2,55,130,229,8,40,1,236,145,77,21,13,39,54,17,88,135,90,45,18,86,24,8,136,88,2,102,254,142,56,32,101,137,44,253,154,2,189,229,115,45,218,59,22,253,67, + 0,72,87,8,32,35,133,247,37,51,17,51,30,3,23,130,192,8,38,35,46,1,39,38,39,17,70,128,31,92,69,43,17,97,128,31,93,34,72,22,2,189,69,210,156,98,38,2,59,253,67,70,210,78,164,49,253,197, + 130,63,48,2,0,58,255,247,2,46,2,198,0,13,0,29,0,0,18,16,72,101,5,46,16,39,38,35,34,7,19,34,17,16,55,54,50,22,23,130,1,8,58,21,16,7,6,164,34,33,77,78,33,34,34,35,76,75,35,110,250,141, + 47,93,54,24,48,32,61,141,50,1,242,254,216,68,65,65,64,1,48,64,67,67,253,193,1,103,1,15,66,23,11,11,22,46,94,176,254,239,65,21,130,102,39,0,2,0,86,0,0,2,46,130,167,41,12,0,26,0,0,19, + 51,50,55,54,79,194,5,67,216,8,8,51,22,20,7,6,43,1,17,187,117,56,33,11,10,39,39,40,78,109,101,218,125,65,35,10,19,16,49,189,117,1,104,21,7,7,37,61,60,37,34,253,144,2,189,53,30,28,49, + 101,39,119,254,230,130,87,36,58,255,159,2,104,130,191,42,23,0,32,0,0,37,30,1,23,21,35,130,251,35,6,34,39,38,137,188,54,16,1,6,16,22,50,54,16,38,34,1,248,26,56,30,64,62,23,24,53,145, + 47,140,135,193,52,254,152,34,68,153,68,69,151,92,29,66,35,59,70,27,27,35,22,66,1,14,136,195,43,254,171,1,125,68,254,217,134,130,1,47,131,130,111,32,72,130,199,32,50,130,199,34,9,0, + 40,134,199,34,52,39,38,135,196,44,21,20,6,7,6,7,30,4,31,1,35,46,2,130,134,32,35,130,25,8,43,17,173,111,82,33,36,38,36,79,109,101,219,123,54,66,23,24,34,56,16,19,6,13,35,28,48,107,54, + 45,16,6,16,13,59,12,24,37,1,119,31,30,124,32,32,131,220,55,52,60,88,45,60,26,32,28,29,34,13,24,66,54,90,105,91,31,11,31,26,1,254,216,68,159,6,36,37,2,203,0,58,74,225,11,130,102,73, + 237,8,43,51,50,23,30,2,23,21,38,47,1,38,34,130,136,36,6,21,20,23,22,130,1,33,30,1,76,96,5,8,108,34,39,46,1,39,79,110,102,118,33,11,29,27,75,14,7,62,40,58,24,47,103,63,79,37,54,16,45, + 26,13,39,21,32,43,87,28,58,21,11,6,8,14,32,69,13,13,39,189,146,48,90,27,57,75,27,130,67,72,23,28,52,27,27,17,3,1,9,16,25,24,43,77,118,57,33,9,4,11,9,4,96,23,7,12,13,7,17,45,21,20,32, + 12,19,13,28,13,3,3,8,38,165,153,40,13,3,5,24,10,0,130,175,43,24,0,0,2,81,2,189,0,7,0,0,33,67,189,6,44,1,2,234,2,57,234,2,110,79,79,253,146,0,131,35,36,60,255,243,2,43,130,35,37,34, + 0,0,5,35,34,132,204,130,164,37,38,53,17,51,17,20,71,116,6,33,55,54,132,13,131,208,60,1,82,29,49,59,10,9,42,12,30,18,4,16,103,21,11,38,75,54,24,56,10,2,101,11,17,34,61,13,130,119,60, + 19,11,25,41,12,48,105,1,176,253,250,43,34,11,32,11,27,52,20,205,1,67,254,80,105,35,58,30,48,132,111,32,29,130,147,32,76,130,111,32,15,130,124,36,2,3,35,38,2,130,114,8,44,51,30,3,23, + 19,2,76,111,107,123,27,80,30,48,33,98,12,31,49,67,22,181,2,189,254,162,254,161,85,1,2,96,155,107,40,106,168,226,75,2,103,0,1,0,2,130,63,32,102,130,63,32,30,130,63,40,3,35,47,1,38,47, + 1,14,2,130,154,34,35,2,3,132,70,8,72,51,18,23,22,23,18,55,2,102,109,99,40,25,10,8,15,4,10,16,12,41,15,98,44,66,86,10,28,21,14,5,86,112,39,14,26,8,58,19,2,189,253,67,244,157,59,51,85, + 22,59,99,75,254,87,1,9,1,180,68,206,153,96,37,2,48,254,255,88,173,43,1,165,140,130,107,32,9,130,107,32,95,130,107,32,31,132,107,36,19,54,55,46,1,132,173,8,83,1,23,19,51,6,7,14,1,7, + 22,23,30,1,23,35,38,39,46,1,1,59,197,109,174,46,30,17,40,31,64,83,108,39,100,44,167,109,28,41,45,95,14,47,113,22,42,15,108,12,16,32,101,1,46,254,210,1,2,68,46,23,56,43,88,119,57,143, + 64,1,8,40,61,68,138,22,75,173,36,64,24,20,26,52,167,130,98,71,223,8,32,19,130,12,33,19,51,130,95,36,6,7,17,35,17,131,94,8,45,39,51,22,23,22,1,52,181,109,29,88,33,51,39,101,29,44,78, + 72,16,108,44,47,64,1,136,1,53,46,143,52,82,63,254,197,1,59,47,71,126,117,25,76,79,107,0,130,62,33,0,55,130,187,32,49,130,187,8,47,16,0,0,51,53,54,18,54,55,33,53,33,21,14,3,7,33,21, + 55,109,180,65,25,254,144,1,229,26,67,106,144,47,1,144,73,156,1,6,95,36,79,71,36,95,150,202,67,80,131,59,44,148,255,166,1,205,3,24,0,7,0,0,23,17,69,243,5,43,148,1,57,220,220,90,3,114, + 74,253,35,75,132,95,8,52,62,255,210,2,40,2,233,0,13,0,0,19,22,23,18,31,1,35,39,46,1,47,1,38,39,158,31,43,212,41,67,96,60,34,62,25,50,121,42,2,233,62,85,254,84,82,134,120,68,124,51, + 102,244,80,31,6,36,154,255,167,1,210,134,95,70,87,5,44,17,154,220,220,1,56,89,75,2,220,74,252,143,132,95,36,61,1,177,2,43,130,191,32,14,130,144,39,7,35,19,51,30,1,23,22,65,106,6,59, + 52,136,111,203,88,25,74,28,58,18,111,8,12,24,75,2,112,191,1,12,32,99,36,77,24,13,16,32,130,252,45,1,255,247,255,167,2,114,255,243,0,3,0,0,7,130,245,49,9,2,123,89,76,76,0,1,0,167,2, + 51,1,169,3,2,0,10,130,83,33,35,38,65,202,6,8,33,22,1,169,112,33,39,64,10,111,9,26,20,48,2,51,46,56,92,13,13,36,29,67,0,0,2,0,68,255,243,2,19,2,27,130,227,32,57,78,1,9,38,61,1,35,34, + 7,6,5,72,159,8,49,55,54,59,1,53,52,39,38,43,1,34,7,53,51,50,62,1,50,130,168,32,22,82,86,5,8,94,35,34,39,54,52,38,160,70,21,27,78,44,38,116,96,42,24,1,31,48,144,84,50,53,35,38,54,59, + 77,107,36,37,61,88,34,40,40,19,32,40,50,24,59,27,76,14,8,1,31,10,42,2,2,158,73,20,7,57,50,81,18,39,22,133,83,44,45,81,67,39,40,13,17,13,68,25,27,2,73,1,1,3,9,15,40,66,36,76,250,24, + 21,1,30,14,15,131,163,32,96,130,163,52,44,2,246,0,25,0,47,0,0,19,20,23,22,50,55,54,55,62,2,55,54,130,147,36,46,1,34,6,7,132,1,79,126,5,68,133,5,32,7,131,186,8,86,6,7,35,17,51,193,70, + 28,69,19,32,18,28,16,4,2,1,43,26,51,46,37,14,30,10,20,4,6,5,48,107,86,52,57,15,3,47,23,43,40,61,106,48,5,4,83,92,1,7,134,50,20,5,8,21,31,54,35,17,17,16,114,47,29,13,10,10,21,22,41, + 25,46,168,80,52,54,121,24,24,110,78,38,27,24,80,44,23,130,136,35,0,1,0,82,130,147,36,3,2,27,0,30,73,199,14,36,22,23,21,38,39,69,25,9,8,57,51,50,55,2,3,72,91,128,73,69,123,62,87,54, + 24,50,33,41,24,45,76,27,47,35,41,76,40,67,91,62,27,40,76,72,128,167,74,35,7,16,17,91,32,9,17,7,12,40,53,91,123,53,28,58,0,2,0,61,130,95,43,9,2,245,0,28,0,51,0,0,55,30,1,130,72,130, + 243,36,1,55,54,52,39,130,95,35,39,38,35,34,130,245,36,14,2,21,20,5,130,234,132,17,32,52,78,236,7,8,37,17,51,17,35,38,200,24,48,22,42,20,37,11,16,6,2,1,1,2,2,3,12,35,80,45,26,29,9,17, + 6,3,1,14,50,101,62,63,17,130,252,8,60,30,29,57,102,75,48,16,11,92,83,2,101,29,13,15,26,29,35,49,17,17,32,17,17,17,30,26,80,20,22,22,37,52,34,16,114,82,80,37,16,54,122,25,88,100,37, + 73,45,15,20,1,42,253,11,19,0,0,0,2,0,62,130,159,32,44,130,255,38,10,0,36,0,0,19,33,132,149,131,147,32,1,87,7,12,35,29,1,33,21,80,85,6,8,67,55,162,1,47,14,21,60,20,26,69,46,40,1,110, + 96,100,129,74,74,116,60,83,170,50,15,254,114,46,46,90,72,76,23,24,1,54,37,42,54,18,5,46,38,254,157,40,72,74,126,164,79,37,148,46,46,56,3,83,48,50,33,10,11,0,0,1,0,66,130,120,44,40, + 2,224,0,22,0,0,51,17,35,53,51,53,131,242,34,59,1,21,131,119,8,35,29,1,51,21,35,17,231,165,165,22,21,43,99,136,140,50,19,19,169,169,1,143,78,71,43,84,21,40,73,19,17,62,88,78,254,113, + 130,62,37,0,76,255,3,2,23,130,183,37,18,0,63,0,0,19,134,155,32,53,131,190,33,46,1,80,245,5,49,23,52,38,53,51,6,29,1,6,20,14,3,35,7,53,51,54,55,131,1,34,53,6,7,132,217,34,39,46,3,77, + 142,5,8,106,168,83,26,33,98,27,8,9,15,13,28,47,27,60,38,38,125,105,55,1,83,1,1,1,17,62,60,45,222,102,53,36,66,34,33,1,32,29,48,40,70,42,20,17,29,32,10,6,48,24,43,42,1,13,147,41,12, + 113,37,33,77,27,50,16,31,13,50,50,173,67,13,29,10,34,45,95,93,193,113,99,66,37,2,78,1,1,2,46,46,60,77,38,9,15,24,13,15,28,72,48,48,24,110,74,37,26,24,131,251,32,97,130,188,35,13,2, + 245,0,130,12,42,19,54,59,1,50,23,22,21,17,35,17,81,160,6,33,7,6,131,12,8,38,51,189,50,106,10,68,38,64,92,57,20,26,73,38,19,7,4,92,92,1,207,80,32,57,127,254,185,1,71,105,23,9,52,26, + 47,20,21,254,214,130,68,35,0,2,0,166,130,84,51,129,2,219,0,21,0,33,0,0,33,35,53,54,61,1,35,53,51,21,20,130,248,34,22,29,1,130,2,41,20,6,17,20,43,1,34,61,1,52,130,109,38,21,1,128,92, + 1,127,218,130,222,63,1,15,71,16,16,71,15,114,28,120,187,77,64,16,35,37,126,11,23,40,16,18,66,16,37,2,103,14,14,81,14,14,130,166,37,0,118,255,4,1,187,130,91,43,15,0,27,0,0,23,53,51, + 50,54,53,17,130,92,41,17,20,6,7,6,35,19,50,29,1,134,88,63,51,118,143,46,44,144,236,22,23,45,80,149,14,14,73,14,14,252,80,80,58,1,227,77,253,208,46,96,25,51,3,215,131,79,32,81,130,81, + 130,170,33,0,113,130,88,34,84,2,218,130,175,8,61,0,1,7,21,35,17,51,17,62,1,55,54,55,51,6,7,30,3,23,35,38,1,20,68,95,95,29,89,33,40,51,111,158,62,31,94,69,44,17,113,186,1,15,60,211, + 2,218,254,90,27,80,30,37,44,141,57,40,121,89,56,22,245,131,75,32,141,130,4,36,119,2,190,0,8,130,7,55,7,17,35,17,52,39,35,53,1,119,2,92,1,139,2,190,192,254,2,2,9,40,63,78,130,26,41, + 0,41,0,0,2,69,2,27,0,38,130,12,33,54,51,65,114,13,32,21,130,55,76,176,7,130,10,8,72,51,20,6,21,54,55,50,1,71,46,85,66,29,28,84,15,12,39,38,25,16,84,3,9,17,30,55,14,15,84,77,1,33,89, + 60,1,217,66,47,50,137,254,207,1,45,114,26,26,29,26,111,254,211,1,96,69,16,30,28,26,112,254,211,2,14,8,29,9,57,2,0,1,0,65,231,5,40,27,0,18,0,0,1,52,35,34,65,217,6,33,22,23,130,127,130, + 114,61,1,177,107,100,28,9,92,83,7,2,51,114,171,92,1,71,137,95,31,40,254,214,2,14,49,30,92,212,254,185,130,174,37,0,69,255,243,2,36,130,179,34,15,0,28,66,223,13,131,75,35,19,34,39,38, + 88,17,6,8,51,16,6,166,81,27,34,67,39,37,82,26,35,69,36,37,143,179,46,15,15,46,178,117,61,62,122,1,7,146,44,14,54,53,97,149,41,13,52,51,254,136,157,51,134,51,159,71,70,254,243,142,0, + 131,95,36,95,255,3,2,42,130,95,32,10,131,95,34,37,50,16,132,162,36,20,23,22,23,34,76,96,6,65,40,5,46,20,7,6,1,82,132,132,101,28,9,79,25,34,105,46,131,177,8,32,48,103,99,60,57,127,39, + 59,1,151,117,37,49,147,43,14,72,80,254,192,3,11,41,26,80,74,73,129,196,62,18,0,2,130,187,40,4,2,15,2,26,0,14,0,32,85,95,15,39,20,1,54,55,51,17,35,17,68,93,10,8,33,190,34,65,66,34,35, + 78,25,32,65,34,35,1,24,7,2,83,92,47,105,136,53,25,127,40,50,101,111,54,54,51,99,151,39,130,191,47,199,1,40,38,28,252,248,1,62,81,130,61,74,206,63,19,130,195,34,1,0,129,130,200,32,25, + 130,195,44,25,0,0,1,38,43,1,34,6,7,6,29,1,130,195,8,53,21,35,17,51,22,20,6,21,54,59,1,22,23,2,25,56,84,39,10,63,21,45,1,1,92,90,1,2,56,127,5,82,49,1,155,48,24,21,45,43,42,30,48,146, + 60,2,14,6,18,24,23,84,2,38,130,82,35,0,1,0,106,70,15,6,39,39,0,0,1,34,21,20,23,75,15,9,86,136,10,33,46,1,84,150,5,8,72,55,54,50,23,21,38,1,67,121,25,28,109,12,139,59,59,183,108,108, + 80,57,36,35,29,27,67,10,9,29,66,40,36,55,54,185,83,80,1,211,78,37,17,17,19,2,26,116,78,45,45,32,90,51,24,21,40,36,19,19,13,2,2,6,12,36,34,60,76,41,40,31,84,43,133,123,41,128,255,254, + 1,230,2,174,0,17,0,74,223,5,36,53,17,51,21,51,130,204,130,210,59,59,1,1,230,169,103,43,43,92,221,221,24,22,60,160,2,39,41,94,2,2,160,79,254,237,59,18,18,130,60,51,1,0,84,255,242,2, + 20,2,13,0,24,0,0,37,6,43,1,38,35,34,130,61,8,61,17,20,51,54,55,54,53,54,53,51,20,22,29,1,35,1,190,37,123,9,4,5,184,91,124,73,30,34,3,92,1,86,57,71,1,213,1,69,254,187,139,8,36,40,57, + 240,83,63,194,72,196,0,0,1,0,50,0,0,2,54,2,14,131,135,40,1,3,35,46,3,39,51,23,22,130,1,8,40,62,1,63,1,2,54,198,119,25,72,55,34,13,95,50,30,22,46,15,26,57,30,50,2,14,253,242,64,194, + 143,91,34,135,81,60,125,40,68,157,81,135,131,67,130,148,33,2,104,130,67,32,27,130,12,43,3,35,2,38,39,51,30,2,23,19,51,23,130,5,131,71,32,51,130,87,8,58,1,39,1,52,89,88,89,33,9,91,22, + 49,16,6,72,102,25,16,22,14,5,11,31,12,31,91,131,88,11,32,12,1,33,254,223,1,100,136,34,93,220,70,27,1,4,80,48,71,44,17,50,151,56,153,253,242,35,107,39,130,93,130,167,32,38,130,167,32, + 67,130,99,39,35,0,0,51,54,55,46,2,130,99,46,3,23,62,1,55,51,14,3,7,30,1,23,35,39,38,130,1,33,7,6,130,1,57,38,140,80,61,92,34,14,103,18,55,40,26,9,109,29,10,103,24,74,55,34,14,25,169, + 27,107,130,198,33,45,17,132,4,58,171,103,79,113,44,16,23,70,52,33,12,139,39,12,31,92,69,43,17,30,211,33,65,38,29,58,21,132,4,133,115,8,40,70,255,2,2,25,2,15,0,50,0,0,23,51,54,59,1, + 50,55,54,53,54,52,39,35,34,38,61,1,52,55,53,50,54,51,17,20,23,22,59,1,17,130,241,34,54,51,21,69,131,5,130,5,8,80,20,6,35,33,38,54,119,59,45,37,102,39,17,25,1,1,189,84,100,1,17,57,18, + 24,25,68,163,1,1,35,11,47,1,1,1,88,66,254,248,1,1,174,1,29,45,64,10,18,8,100,63,252,32,28,50,1,254,202,77,31,27,1,82,54,52,1,83,41,37,76,40,66,115,51,32,32,81,127,21,39,0,130,255,32, + 101,130,255,32,8,130,139,32,17,130,255,33,53,54,88,38,5,32,21,131,238,8,34,6,7,33,21,101,39,60,114,109,254,203,1,150,39,59,115,60,27,21,1,65,80,46,70,134,129,68,78,46,70,136,69,33, + 24,71,131,63,40,92,255,151,2,3,3,38,0,44,66,111,6,130,193,83,72,8,130,11,32,55,130,222,71,147,6,32,20,84,214,5,33,29,1,132,219,8,76,2,3,33,122,44,43,26,27,56,72,59,71,25,26,43,43,123, + 33,36,68,22,21,23,26,48,46,28,23,21,24,46,56,105,34,35,109,113,70,28,23,88,21,28,72,111,109,35,35,84,14,19,71,110,74,33,40,11,10,41,33,76,115,63,19,15,0,1,1,6,255,181,1,92,3,9,0,3, + 130,123,43,17,51,17,1,6,86,75,3,84,252,172,0,131,151,32,102,130,151,32,16,130,151,40,50,0,0,23,53,51,50,55,54,132,140,34,55,38,39,130,1,137,163,33,23,22,132,142,35,23,22,51,22,72,59, + 8,130,167,8,99,35,102,34,72,20,22,22,31,43,28,16,37,9,6,22,20,72,34,31,124,43,42,10,6,7,33,65,16,21,28,32,96,35,23,42,42,125,105,83,16,20,70,108,74,34,42,9,6,15,31,40,26,59,97,63,20, + 14,84,35,34,110,113,36,24,11,10,37,1,88,39,33,61,100,109,36,34,0,1,0,40,0,241,2,61,1,215,0,33,0,0,19,39,54,55,54,51,50,23,30,1,130,115,130,149,33,55,23,87,136,6,131,151,8,89,35,34, + 7,14,1,118,78,12,21,41,76,37,19,28,41,11,57,36,56,13,5,3,77,33,36,83,39,22,33,28,51,16,19,19,38,17,7,12,1,20,18,71,36,70,11,15,41,14,76,73,22,27,17,51,59,68,12,19,37,65,10,13,33,27, + 44,0,2,0,232,255,227,1,138,2,193,0,6,0,20,0,0,55,54,55,51,19,21,35,2,52,131,115,33,22,21,133,106,8,34,246,9,29,60,34,132,14,23,21,37,35,46,49,15,17,37,21,181,79,187,254,246,210,2,102, + 76,21,23,46,27,66,18,5,21,0,130,71,48,105,255,109,2,18,2,161,0,8,0,43,0,0,37,17,6,7,79,220,5,34,53,38,39,89,241,5,36,54,55,53,51,21,130,197,32,21,130,16,32,39,71,127,5,32,21,130,36, + 37,35,21,1,93,66,40,130,0,8,70,66,117,60,67,13,22,24,70,115,51,62,49,19,46,23,34,27,8,16,8,48,50,11,9,55,55,59,1,151,6,56,54,87,88,54,56,212,135,9,75,75,115,58,37,60,28,82,12,134,134, + 8,17,8,81,26,5,8,3,254,102,1,3,1,10,28,81,4,4,23,135,0,82,115,8,43,44,2,203,0,27,0,0,19,52,51,50,23,131,115,73,231,8,8,47,21,33,21,33,53,51,53,35,53,51,187,227,62,70,9,8,50,60,103, + 20,8,185,185,1,12,254,26,117,99,99,1,221,238,28,86,5,5,31,96,33,61,86,68,218,80,80,218,68,130,219,42,102,0,92,2,38,2,0,0,13,0,47,70,91,11,8,32,34,7,6,20,23,7,38,39,54,55,46,1,53,52, + 55,38,39,55,23,54,50,23,55,23,22,23,7,22,20,7,22,23,6,130,27,8,86,6,34,250,32,45,46,30,31,31,30,92,30,32,11,82,10,36,80,3,22,7,30,72,12,48,82,50,90,50,83,22,14,9,83,30,31,23,61,38, + 9,80,3,50,90,229,28,28,31,42,43,29,28,28,28,86,92,76,10,32,75,3,36,43,11,44,43,69,10,44,79,29,28,78,22,13,8,79,49,88,39,21,57,33,11,75,3,29,133,231,86,239,7,43,35,0,0,1,6,7,21,51,21, + 35,21,35,131,216,33,53,39,130,221,79,22,8,8,80,23,19,51,7,14,1,7,51,21,1,145,5,38,201,201,101,200,200,45,155,122,19,30,68,33,11,108,44,47,64,27,181,109,50,51,53,11,126,1,130,9,62,17, + 51,247,247,51,17,71,52,32,48,111,55,17,76,79,107,47,1,53,80,80,86,17,52,0,0,2,1,10,255,188,1,96,3,2,0,3,0,7,67,61,5,8,45,3,17,51,17,1,10,86,86,86,68,1,101,254,155,1,226,1,100,254,156, + 0,2,0,99,255,164,2,6,2,203,0,17,0,85,0,0,19,20,23,22,23,54,53,52,39,38,130,1,33,6,7,130,1,32,5,92,10,8,37,43,1,39,46,3,39,70,224,11,35,2,53,52,55,66,44,6,35,51,31,1,22,130,58,86,216, + 5,66,74,6,33,30,1,130,17,8,129,22,181,42,40,112,61,8,12,17,39,117,28,18,4,3,9,1,81,92,50,5,4,51,50,86,20,20,27,18,20,43,11,88,60,73,26,7,61,38,46,87,46,16,92,59,50,35,61,18,18,39,20, + 9,13,20,53,82,60,42,13,33,7,7,18,26,59,5,5,20,92,36,33,1,98,35,37,36,53,38,48,20,13,20,15,35,58,18,25,4,5,19,77,84,48,44,32,14,25,58,39,40,2,3,4,3,12,3,77,36,43,13,15,44,36,25,25,47, + 50,41,25,87,46,39,67,61,38,27,7,3,2,130,0,8,40,3,17,77,37,7,20,15,16,11,22,18,26,33,3,2,10,46,41,37,0,0,0,2,0,160,2,123,1,201,2,217,0,11,0,23,0,0,1,52,59,1,74,174,6,33,53,39,138,11, + 36,1,99,16,70,16,130,2,43,195,14,72,15,15,72,14,2,204,13,13,68,135,2,33,0,3,130,71,51,58,2,104,2,126,0,27,0,51,0,75,0,0,37,50,55,21,6,34,39,91,150,5,46,51,50,23,21,46,2,43,1,34,7,6, + 21,20,22,7,82,126,9,75,202,9,34,20,23,22,79,122,7,68,56,9,89,249,5,8,126,1,85,45,46,52,106,36,73,28,12,13,42,139,57,56,35,43,14,6,13,64,31,33,70,150,55,90,24,26,74,68,64,33,18,76,70, + 111,56,32,137,27,5,32,9,1,105,53,92,160,96,52,35,17,91,165,99,72,45,34,8,50,35,35,42,26,219,25,49,22,11,24,68,31,82,32,103,20,51,17,6,2,34,33,64,60,68,26,63,19,5,42,37,72,42,37,116, + 68,70,14,56,123,23,24,54,64,15,124,35,130,73,87,61,76,24,129,48,31,41,12,70,87,83,52,60,28,20,0,130,219,38,135,0,220,1,224,2,203,130,9,41,17,0,46,0,0,55,53,33,21,3,130,203,130,179, + 33,50,55,130,173,34,61,1,21,73,162,8,80,51,5,8,122,34,7,53,54,63,1,50,23,22,29,1,35,38,146,1,78,138,104,22,8,22,22,62,22,43,18,9,53,80,96,33,9,103,33,56,79,49,27,93,71,40,60,35,132, + 33,11,64,4,220,59,59,1,43,37,13,49,18,18,8,18,46,22,19,22,130,61,70,22,28,89,28,8,1,55,20,11,31,59,16,9,2,92,31,44,211,18,0,0,0,2,0,40,0,67,1,245,1,242,0,12,0,29,0,0,1,23,21,39,53, + 62,3,55,21,14,1,7,22,23,21,38,39,46,1,130,15,32,1,130,148,8,49,21,6,1,94,151,235,29,86,64,40,16,29,93,255,78,72,15,20,53,118,28,28,87,32,61,26,78,1,26,126,89,196,38,24,72,54,34,13, + 90,24,79,23,67,59,89,12,17,45,98,24,130,17,53,27,52,22,90,67,0,1,0,44,0,174,2,60,1,150,0,5,0,0,37,53,33,130,237,41,1,232,254,68,2,16,174,151,81,232,130,131,42,1,0,178,0,225,1,182,1, + 46,0,3,65,11,5,39,178,1,4,225,77,77,0,4,66,3,8,39,10,0,37,0,61,0,85,0,86,143,5,32,53,85,200,8,41,21,20,7,6,7,30,1,23,30,5,81,241,5,35,43,1,21,39,66,12,46,8,32,246,51,45,14,23,19,18, + 42,54,65,119,145,48,16,20,5,11,8,3,8,11,8,18,32,11,72,65,10,18,26,16,23,133,66,20,39,8,32,1,113,9,15,30,33,9,12,254,225,1,71,93,50,24,7,3,1,10,7,4,11,12,11,27,50,17,102,12,19,7,140, + 3,66,31,40,8,40,1,0,159,2,135,1,202,2,205,0,3,0,0,1,21,33,53,1,202,254,213,2,205,70,70,0,0,0,2,0,150,1,160,1,211,2,203,0,13,0,35,130,29,80,201,5,46,35,34,7,6,20,22,50,55,22,23,30,1, + 20,14,1,103,70,13,8,61,1,132,15,6,13,38,14,22,45,24,28,54,92,11,30,31,13,23,23,34,18,32,54,84,46,26,45,46,66,8,7,35,2,4,17,50,16,30,17,7,24,26,76,52,228,13,29,13,55,60,54,30,12,21, + 63,39,38,72,43,44,1,5,132,115,32,44,130,120,34,60,2,38,130,143,38,15,0,0,51,53,33,21,101,69,11,47,44,2,16,254,206,222,222,84,222,222,80,80,160,155,80,155,130,2,130,152,47,0,161,2,14, + 1,190,3,159,0,27,0,0,19,54,51,50,130,153,38,21,20,7,14,2,7,51,130,208,33,54,55,130,1,8,52,52,39,38,34,7,164,66,46,100,43,18,6,15,40,67,59,20,204,254,227,21,36,114,15,21,7,23,106,68, + 3,132,27,48,20,32,10,35,19,51,60,54,18,54,53,20,33,100,27,29,38,12,38,34,130,142,50,0,1,0,163,2,9,1,206,3,161,0,55,0,0,1,20,7,22,23,88,146,5,36,34,47,1,38,39,131,1,32,53,94,4,10,92, + 154,10,38,35,34,14,3,7,53,54,130,139,8,92,22,1,196,86,47,24,25,12,36,133,17,7,7,14,8,7,17,10,20,11,39,24,23,26,95,14,2,28,23,59,32,37,71,18,6,52,17,17,23,39,15,14,14,8,77,38,112,37, + 11,3,59,65,22,8,26,25,67,22,71,1,2,1,1,4,2,4,5,56,15,3,7,56,7,13,29,18,18,51,33,10,13,42,12,4,8,2,5,6,2,57,17,60,19,130,159,40,238,2,81,1,221,3,2,0,9,130,159,34,6,7,35,132,238,53,55, + 1,221,143,20,76,17,25,26,20,38,14,3,2,156,21,21,33,33,24,48,18,133,207,44,83,255,55,2,65,2,14,0,30,0,0,37,6,78,3,5,39,17,20,23,22,51,50,53,17,74,209,6,33,50,55,68,18,5,8,47,1,175,42, + 181,42,83,91,18,33,66,127,93,17,6,5,8,7,23,40,23,39,14,24,60,73,72,254,252,2,215,254,186,54,30,55,158,1,51,254,106,42,8,3,10,70,20,14,24,133,95,36,53,255,164,2,3,85,35,5,43,5,17,38, + 39,38,53,52,54,59,1,17,35,130,1,56,1,23,136,56,34,132,106,224,71,95,92,1,141,10,78,43,67,90,108,252,231,2,224,253,32,130,56,48,1,0,227,1,14,1,133,1,174,0,15,0,0,1,22,21,20,107,176, + 9,8,35,51,50,1,110,23,35,29,17,34,23,24,50,14,17,32,1,152,23,43,26,34,12,23,22,35,54,20,6,0,1,0,198,255,71,1,148,130,65,41,17,0,0,5,50,53,52,38,39,51,130,61,8,33,7,6,43,1,34,39,53, + 22,1,17,62,33,11,59,54,46,33,25,37,28,37,36,128,43,21,49,15,54,49,53,18,11,11,61,131,102,8,57,0,172,2,14,1,202,3,151,0,13,0,0,19,54,55,51,17,51,21,33,53,51,17,14,1,7,172,42,73,68,103, + 254,235,103,21,70,21,3,132,6,13,254,172,53,53,1,30,4,11,4,0,3,0,122,0,220,1,239,69,79,6,32,37,68,67,5,34,39,22,51,101,144,6,106,122,9,32,39,103,204,10,8,71,21,20,138,1,82,213,20,25, + 53,27,29,44,21,70,20,62,244,49,87,46,25,43,16,45,8,3,76,43,67,87,49,51,220,59,59,172,9,37,33,68,79,38,21,9,27,203,39,53,11,19,17,46,57,20,34,98,54,31,53,53,88,89,0,2,0,116,0,67,2,65, + 1,242,91,143,5,34,1,23,21,130,161,41,6,7,53,62,1,55,38,39,7,38,130,230,33,23,22,130,19,8,61,3,7,53,54,1,86,235,29,86,32,59,29,28,94,29,54,97,76,72,78,59,60,78,37,15,40,64,87,28,72, + 1,242,197,38,24,72,27,50,23,89,23,79,24,42,84,126,59,67,90,50,50,65,32,38,13,33,54,72,24,89,59,0,130,0,59,4,0,13,255,129,2,45,3,11,0,5,0,20,0,24,0,36,0,0,37,51,53,6,7,6,23,53,35,131, + 108,44,54,55,51,21,51,21,35,21,19,5,39,37,1,65,45,8,8,65,6,7,1,27,137,9,12,68,89,194,22,67,25,34,34,80,59,59,69,253,237,13,2,19,254,11,115,68,103,254,235,103,63,49,12,195,13,16,94, + 211,87,57,31,91,33,47,46,253,52,87,1,231,123,51,123,1,95,17,254,172,52,52,1,31,12,7,130,122,33,0,3,136,123,36,30,0,34,0,46,130,121,80,150,8,37,7,6,7,51,21,33,133,128,92,31,6,34,34, + 7,37,143,131,59,8,63,48,82,44,43,15,29,39,83,36,205,254,226,22,72,19,54,9,6,6,19,25,23,96,60,1,37,142,143,58,247,26,30,34,47,29,22,40,36,75,34,53,52,20,66,17,50,12,9,6,32,18,33,17, + 17,34,172,144,152,65,19,7,32,19,65,19,6,32,79,65,19,25,47,45,1,50,52,35,34,15,1,14,1,7,53,55,62,1,55,137,192,32,22,75,212,5,41,47,1,34,39,46,2,39,53,30,1,101,182,8,34,43,1,53,65,61, + 22,8,45,135,95,90,37,17,25,7,15,8,18,21,19,11,16,35,71,42,42,49,16,21,96,47,48,85,9,8,29,8,7,13,23,14,8,18,24,7,38,27,93,14,2,6,21,82,33,65,98,20,8,56,212,113,5,7,2,5,2,56,4,5,2,2, + 3,27,28,46,51,23,7,5,20,79,58,31,32,1,2,2,4,4,4,2,58,6,8,2,10,56,8,25,12,40,51,0,2,0,96,255,244,1,239,2,219,0,50,0,67,130,227,33,54,55,66,100,5,72,27,7,35,62,1,63,1,131,211,36,55,54, + 61,1,52,130,3,32,51,131,210,36,7,14,2,15,1,81,137,5,33,51,19,69,25,5,33,35,34,66,247,6,8,119,50,1,82,68,89,31,31,11,33,25,35,38,87,52,56,17,22,53,6,27,8,12,5,15,4,8,1,96,12,18,43,12, + 11,27,5,13,31,19,28,71,40,30,10,4,16,25,23,39,31,6,5,57,7,24,65,7,57,89,17,11,4,14,3,3,44,46,75,40,27,35,49,5,25,7,13,6,17,8,16,32,2,3,5,5,65,72,45,25,40,39,11,10,26,5,13,36,25,10, + 31,22,38,2,149,9,27,13,51,14,23,29,13,13,16,56,14,1,0,0,0,3,90,175,5,39,3,101,0,10,0,23,0,31,98,145,23,39,3,46,1,39,51,30,1,23,98,153,23,39,145,22,70,22,92,19,61,19,98,161,22,36,2, + 49,23,79,23,130,2,137,111,32,104,132,111,32,27,151,111,35,17,7,35,55,151,107,34,114,77,98,151,102,34,177,123,123,138,211,32,102,132,99,32,35,151,99,42,3,38,39,14,1,7,35,54,55,51,22, + 152,215,43,47,48,35,15,52,16,69,85,20,94,44,62,151,116,41,50,51,33,15,53,16,102,23,51,74,138,123,32,81,132,123,32,62,153,123,40,34,7,6,29,1,35,52,55,54,94,110,5,32,22,79,205,5,43,51, + 6,7,6,35,34,46,1,47,1,46,3,151,255,8,33,172,14,18,6,22,63,27,26,42,6,9,30,34,7,10,26,11,19,8,13,62,3,53,17,15,32,18,9,6,14,7,7,18,5,151,169,63,92,7,4,11,30,4,49,27,28,1,6,17,4,5,13, + 9,15,19,3,73,23,8,8,3,3,7,4,4,9,4,0,4,66,11,12,34,35,0,47,151,189,32,19,86,222,6,36,59,1,50,21,7,138,11,151,177,32,19,76,76,5,38,196,15,72,14,14,72,15,151,157,35,93,14,14,67,135,2, + 66,151,8,38,126,0,8,0,22,0,44,132,137,53,1,39,14,3,18,6,20,30,1,51,50,55,54,52,39,38,35,34,3,7,35,19,110,2,8,8,89,21,20,7,22,18,22,23,35,39,202,213,48,46,13,13,39,29,18,35,12,34,26, + 16,33,21,22,5,20,52,29,101,54,105,214,61,14,46,77,56,42,39,61,46,120,34,14,104,56,1,3,158,156,43,43,132,97,62,2,11,26,44,31,11,21,20,45,13,44,253,115,183,2,145,35,72,38,22,70,38,39, + 53,66,41,138,254,141,105,43,183,0,130,0,32,2,132,4,38,78,2,189,0,7,0,23,131,141,46,17,35,14,1,7,6,15,1,35,19,33,21,35,21,51,132,3,63,33,53,164,154,53,12,38,13,30,30,50,92,205,1,120, + 169,153,153,178,254,240,1,3,1,107,45,133,50,106,108,180,2,100,83,5,32,180,131,74,45,1,0,70,255,71,2,25,2,203,0,51,0,0,1,130,202,78,63,5,131,217,43,55,21,6,7,34,6,7,22,23,22,21,20,71, + 149,5,37,51,50,53,52,46,3,71,33,7,8,88,55,54,50,23,2,25,79,87,98,49,49,75,45,76,59,68,20,19,47,43,6,22,8,26,3,15,120,22,25,40,36,39,62,14,3,7,7,4,129,73,78,14,45,116,55,159,78,2,67, + 58,71,74,142,175,70,42,34,10,15,97,23,9,3,1,34,7,32,19,82,11,61,15,43,14,26,8,9,9,6,8,87,96,171,89,50,152,50,25,39,132,226,43,0,98,0,0,2,39,3,104,0,11,0,19,101,29,13,68,3,7,50,98,1, + 188,254,170,1,70,254,186,1,95,240,21,71,21,92,18,62,18,134,217,36,2,237,23,77,23,130,2,130,72,32,2,138,71,32,15,142,71,34,7,35,55,138,67,35,113,114,77,98,134,63,35,3,104,123,123,138, + 59,32,102,130,131,32,24,142,59,67,179,9,33,31,1,138,68,32,151,67,167,7,35,95,20,33,52,134,77,33,2,233,67,152,6,36,23,39,63,0,3,134,143,32,101,130,83,33,25,0,130,234,101,247,11,32,3, + 66,195,6,33,55,54,66,209,11,133,13,138,100,44,85,15,71,15,3,4,28,51,15,196,15,72,14,132,8,134,105,41,3,20,14,14,67,7,4,3,14,67,134,7,36,0,0,2,0,100,130,4,34,3,3,104,130,111,32,18,130, + 195,95,33,5,43,21,35,17,51,21,3,46,1,39,51,22,23,101,128,6,38,216,21,70,22,92,47,52,101,135,7,38,2,237,23,77,23,60,63,140,63,32,15,142,63,34,7,35,55,134,60,35,77,114,77,98,135,57,65, + 60,5,135,119,32,102,130,119,32,21,144,55,33,54,55,130,120,33,35,38,134,61,41,208,82,70,85,20,95,44,62,71,48,136,67,41,61,84,102,23,51,74,51,0,0,3,134,191,32,101,130,71,34,23,0,35,142, + 73,67,255,22,134,87,32,58,67,238,12,136,91,32,20,67,223,11,42,2,0,4,0,0,2,39,2,189,0,19,131,93,38,55,51,50,55,62,1,55,81,133,6,47,43,1,21,51,21,35,3,17,35,53,51,17,51,50,22,21,106, + 19,5,8,54,170,48,66,45,43,36,10,28,9,21,50,54,94,48,132,132,103,63,63,151,171,162,81,45,59,64,99,78,15,17,43,21,57,120,85,36,87,32,33,221,70,254,179,1,77,70,1,42,170,181,179,85,45, + 21,20,130,102,33,0,70,130,4,38,35,3,102,0,16,0,60,130,201,32,17,96,135,5,41,17,51,17,35,38,39,38,39,17,19,69,134,5,33,30,1,84,192,8,48,61,1,51,6,7,6,34,39,46,4,47,1,46,1,39,46,1,80, + 206,5,8,121,70,128,16,44,34,93,65,97,128,74,59,93,26,39,63,27,25,43,28,31,20,3,10,24,4,11,7,15,9,11,62,3,53,17,28,8,18,11,9,12,15,3,18,4,5,3,7,6,3,7,26,9,2,2,189,37,100,78,215,147, + 2,65,253,67,167,137,215,59,253,190,3,0,44,31,27,13,11,1,3,13,1,4,13,11,19,3,72,23,7,1,2,4,3,6,7,2,10,1,4,1,3,3,27,8,6,0,0,3,0,58,255,243,2,46,3,104,0,13,0,26,0,34,83,209,11,43,35,34, + 6,16,39,16,51,50,23,22,21,16,131,164,33,38,19,130,162,131,210,8,53,198,35,75,76,35,34,34,34,77,76,68,106,250,126,63,61,192,27,94,47,140,246,21,70,22,92,18,63,18,130,67,67,64,156,157, + 64,66,132,254,205,152,1,109,90,94,181,254,190,36,5,22,66,2,162,67,178,8,142,115,32,30,156,115,35,1,7,35,55,148,111,36,1,129,114,77,98,150,108,34,3,29,123,71,159,5,133,219,32,102,132, + 219,32,39,157,103,67,240,11,149,112,32,86,67,251,11,150,121,33,2,158,68,11,12,139,127,32,70,156,127,66,27,40,148,155,32,148,66,35,38,151,181,32,181,66,41,34,34,0,0,4,66,43,6,40,101, + 0,13,0,26,0,38,0,50,156,209,32,1,67,188,22,148,192,33,1,143,67,203,12,151,167,32,201,67,218,11,8,34,0,1,0,75,0,82,2,30,2,9,0,36,0,0,37,7,39,54,63,1,54,55,46,3,39,55,30,2,23,54,55,23, + 14,3,7,109,205,5,58,7,38,39,46,1,1,52,174,59,21,32,64,40,18,22,64,48,30,11,58,55,79,30,11,47,128,59,132,12,63,11,30,24,56,32,21,59,11,15,57,69,247,165,55,20,30,60,37,17,20,61,45,28, + 11,55,50,76,28,11,43,122,55,132,12,43,11,28,22,53,30,20,55,11,14,53,67,0,130,0,52,3,0,4,255,223,2,88,2,221,0,18,0,33,0,72,0,0,9,1,30,2,78,109,5,51,38,53,39,38,53,38,39,52,5,54,55,38, + 35,34,7,6,7,6,29,1,68,91,5,34,46,1,39,89,67,5,32,53,73,143,5,41,55,22,23,7,22,23,30,1,23,22,68,121,5,8,134,34,38,39,38,1,186,254,255,15,50,37,21,78,33,34,1,2,1,1,1,254,234,168,84,23, + 91,42,19,36,13,33,2,39,44,28,10,31,10,89,24,5,1,1,4,98,63,88,116,57,70,35,15,84,14,4,13,6,2,3,16,30,92,46,68,49,42,17,41,1,231,254,167,35,33,11,67,73,148,13,23,10,37,8,7,7,6,10,251, + 225,113,78,13,27,30,62,182,47,14,10,18,172,56,39,6,21,6,119,56,69,16,16,39,41,221,88,50,77,95,23,12,112,28,15,49,51,18,27,140,68,133,48,22,14,7,16,130,222,50,0,2,0,74,255,243,2,31, + 3,102,0,34,0,41,0,0,5,34,39,81,86,5,33,38,39,80,141,5,37,51,50,55,62,1,53,131,11,130,223,130,227,37,14,1,3,38,39,51,130,202,8,77,1,46,67,42,39,13,34,9,19,2,2,1,101,11,33,89,85,35,11, + 2,102,22,12,17,30,10,10,48,71,21,49,65,93,18,61,19,13,21,17,12,31,25,41,42,18,23,1,228,254,37,80,20,63,57,19,61,191,1,54,254,80,102,73,19,27,20,5,5,27,4,2,247,57,67,23,77,24,0,136, + 135,32,104,130,135,32,40,164,135,37,19,6,7,35,54,55,156,134,37,106,56,59,76,65,33,161,133,37,3,117,61,62,83,40,65,11,12,32,46,165,131,68,77,9,32,23,156,137,32,68,75,247,10,161,143, + 33,2,246,76,2,11,33,0,3,65,163,6,38,101,0,34,0,46,0,58,165,153,67,149,22,156,165,32,134,67,156,12,161,167,33,3,33,67,167,12,32,2,75,239,6,36,102,0,19,0,24,104,173,21,36,19,7,35,54, + 55,104,178,18,36,148,115,76,44,54,104,183,20,37,1,175,124,58,66,0,72,191,6,42,71,2,189,0,8,0,23,0,0,37,50,86,24,5,8,48,21,7,17,51,21,51,50,23,22,21,20,7,6,43,1,21,1,62,158,60,36,75, + 103,102,102,127,192,46,16,63,65,126,127,250,124,75,30,18,247,250,2,189,127,112,38,38,117,47,50,92,247,5,40,94,255,243,2,62,2,219,0,60,68,217,9,66,246,6,34,53,52,55,67,206,6,35,21,17, + 35,17,130,12,130,91,39,23,6,7,6,21,20,31,2,109,115,5,67,194,9,8,110,39,229,75,60,84,26,7,22,8,26,70,84,6,1,90,30,39,4,114,91,20,7,93,119,39,105,39,116,3,116,35,11,24,10,38,22,32,45, + 19,31,30,10,16,55,97,50,41,9,14,23,86,29,52,12,20,32,32,7,25,35,53,55,10,12,89,42,15,8,103,70,23,34,253,236,2,23,151,33,12,13,36,156,9,60,19,18,38,19,10,25,11,22,27,26,45,54,56,35, + 14,10,41,10,2,3,7,0,0,0,3,0,68,130,179,40,48,3,18,0,13,0,66,0,77,130,183,40,20,23,22,50,54,55,54,61,1,98,167,5,33,7,53,102,237,5,32,51,104,105,8,93,181,6,41,30,1,23,35,38,53,38,39, + 6,7,83,210,9,88,104,5,32,39,95,200,6,8,130,160,88,13,52,60,21,44,116,123,29,10,138,97,92,10,12,20,43,18,37,43,92,55,58,16,3,3,12,1,10,3,4,6,4,1,92,18,2,2,15,44,39,61,37,82,49,51,63, + 62,122,123,76,27,32,50,73,28,11,99,9,24,39,52,17,159,84,15,2,26,25,48,89,18,58,20,1,24,50,87,4,3,5,13,4,7,30,31,44,8,8,30,80,115,14,13,22,35,40,26,10,17,14,2,42,19,9,10,30,28,23,12, + 45,39,83,86,47,46,13,92,20,8,143,55,80,31,11,11,30,49,65,22,142,223,32,72,196,223,68,33,5,180,221,37,143,12,150,77,65,76,184,216,38,1,64,12,165,84,93,0,65,183,16,32,78,196,215,43,3, + 7,35,55,51,30,1,23,35,38,39,38,180,221,43,26,90,69,123,73,23,77,23,69,17,28,29,185,227,40,3,116,177,33,111,33,22,36,36,138,231,39,2,250,0,13,0,66,0,103,196,231,36,55,50,53,51,20,88, + 223,5,33,47,1,130,235,32,35,71,102,5,75,93,5,36,23,30,4,23,22,180,253,8,33,40,37,62,56,18,16,26,12,20,19,10,12,7,14,4,5,10,29,6,2,1,62,26,25,46,18,18,23,18,7,20,10,4,6,65,19,56,41, + 223,73,94,30,8,5,8,15,10,9,130,88,49,39,14,19,61,36,35,6,13,16,6,18,7,2,5,0,0,0,4,67,195,5,41,2,217,0,13,0,66,0,78,0,90,65,37,69,69,233,22,65,27,52,35,123,15,71,15,130,2,32,196,93, + 166,5,65,7,56,32,182,93,222,11,135,247,33,3,77,132,247,34,82,0,96,196,247,48,2,14,1,20,30,1,51,50,54,55,54,53,52,46,1,34,23,85,42,6,37,54,51,50,23,22,20,180,253,55,68,22,11,33,26,16, + 32,33,6,5,32,28,32,115,40,59,56,40,41,82,55,58,41,39,65,7,56,8,47,1,55,20,26,45,30,11,31,12,12,15,31,31,10,162,38,38,37,54,55,74,37,35,113,0,0,3,0,20,255,243,2,88,2,27,0,14,0,26,0, + 80,0,0,55,6,7,6,20,86,151,5,41,61,1,35,34,55,51,53,52,38,39,130,214,38,7,6,21,19,50,55,21,130,29,36,35,34,39,38,39,134,7,34,53,52,55,110,97,6,37,35,34,7,6,7,53,131,244,132,248,35,29, + 1,35,21,86,17,5,8,132,176,51,15,8,23,22,32,67,15,10,24,42,149,164,15,4,18,33,33,16,45,108,75,52,51,42,17,23,80,40,14,8,31,58,21,32,90,39,23,112,36,46,60,24,23,49,49,60,8,8,72,63,91, + 34,34,94,89,34,36,246,1,60,23,236,9,36,18,70,21,24,50,33,46,55,67,24,57,38,7,25,7,8,23,107,254,241,48,80,32,5,2,43,13,18,55,15,4,63,37,56,122,34,10,41,56,30,31,30,4,5,79,32,60,60,56, + 54,145,42,17,1,6,10,123,18,9,0,1,0,82,255,71,2,3,130,223,34,56,0,0,90,23,5,103,203,9,32,55,132,170,36,23,21,38,39,38,130,189,32,21,95,10,7,82,168,7,71,203,5,8,94,34,39,53,22,1,53,63, + 14,2,4,11,4,116,66,73,14,25,36,74,125,67,52,19,21,56,57,20,66,34,103,104,34,56,11,70,46,7,8,40,46,5,20,7,44,46,33,26,36,26,40,36,128,43,16,24,4,8,14,6,6,66,73,131,71,36,61,35,73,22, + 8,10,91,47,8,3,14,40,149,150,40,14,1,10,34,6,7,90,25,9,3,1,48,44,51,20,90,130,5,54,0,3,0,62,255,243,2,44,3,18,0,12,0,39,0,50,0,0,19,51,54,59,1,130,152,132,154,65,121,6,34,43,1,34,119, + 116,8,33,22,29,109,161,5,32,3,71,60,6,8,34,162,64,25,22,192,2,8,12,17,35,136,42,43,170,87,108,74,75,12,12,23,129,75,73,111,66,86,110,61,60,254,114,140,19,8,71,41,6,8,45,38,52,17,1, + 53,1,44,20,34,19,39,42,41,254,188,54,86,32,6,2,73,73,129,161,74,42,67,68,119,42,3,150,28,3,2,38,55,80,31,11,11,31,48,65,22,0,130,0,141,155,32,47,169,155,37,19,7,35,62,3,55,161,153, + 39,167,162,77,17,51,39,24,9,159,150,38,215,177,22,65,48,31,11,145,147,32,51,169,147,70,180,11,161,153,32,2,70,161,8,33,29,28,159,157,32,154,70,135,11,32,4,65,207,5,41,2,217,0,12,0, + 39,0,51,0,63,169,161,86,133,23,161,173,38,147,15,71,16,16,71,15,69,53,6,159,175,32,77,69,28,11,50,2,0,109,255,252,2,34,3,6,0,14,0,27,0,0,5,35,34,38,109,250,6,37,23,22,59,1,1,38,73, + 163,7,8,54,2,34,117,81,92,122,214,52,16,22,108,254,237,20,29,30,45,28,10,99,9,24,38,52,17,4,100,91,1,16,67,254,173,86,24,7,2,15,21,33,33,48,31,11,11,31,48,66,21,0,0,0,2,0,134,136,91, + 130,15,142,91,37,3,7,35,62,2,55,138,87,38,50,162,78,27,81,24,9,139,80,35,192,177,34,101,65,247,5,139,75,32,25,145,167,84,8,6,34,39,6,7,138,80,43,254,100,92,31,74,92,31,70,68,22,68, + 22,139,85,40,16,132,44,132,44,86,29,86,29,130,163,32,3,133,163,33,2,217,130,255,34,26,0,38,144,89,85,74,7,85,72,11,131,11,138,102,35,100,15,71,15,130,2,38,195,15,72,14,14,72,15,139, + 104,32,66,70,134,12,34,0,2,0,109,235,5,46,219,0,46,0,65,0,0,1,22,23,14,1,7,22,21,72,98,6,69,82,5,43,22,59,2,50,23,46,2,39,38,39,46,1,130,205,34,39,54,55,85,93,5,32,3,112,248,11,8,123, + 38,43,1,34,7,6,1,211,10,6,23,78,24,190,98,56,86,112,65,62,61,62,123,17,5,2,2,1,2,4,1,8,11,5,7,8,16,11,5,15,124,15,79,40,17,58,16,109,36,28,157,37,37,68,67,39,37,55,7,9,25,13,23,113, + 30,10,2,196,30,16,7,24,7,190,186,151,72,38,69,72,123,127,67,70,1,1,2,9,13,7,10,9,17,12,5,5,38,46,25,12,18,58,18,39,28,254,99,92,49,51,51,51,89,113,68,4,2,6,109,36,0,0,0,2,110,239,6, + 39,225,0,18,0,53,0,0,19,110,229,5,32,17,110,248,10,73,53,10,32,39,131,200,32,35,130,170,32,7,73,51,6,80,132,5,8,88,189,51,115,170,92,110,97,28,9,92,83,7,186,38,62,26,25,45,43,38,4, + 9,11,8,4,3,7,8,17,10,7,2,63,27,25,45,34,36,7,3,19,16,3,5,1,191,92,212,254,185,1,71,137,95,30,41,254,214,2,14,49,188,72,65,33,35,38,3,9,10,5,3,2,3,18,11,44,63,35,35,28,7,4,18,9,3,3, + 0,3,65,95,5,43,3,18,0,13,0,27,0,40,0,0,1,34,114,203,7,36,53,52,39,38,3,69,68,10,37,16,6,3,46,1,39,130,2,8,69,51,30,3,23,1,52,142,80,27,35,106,27,10,81,26,36,175,49,15,136,45,58,117, + 61,62,122,124,20,60,28,16,28,11,100,9,24,38,52,17,1,210,203,150,41,13,116,38,50,151,39,13,254,33,158,51,67,202,56,18,71,72,254,243,140,2,110,22,65,32,16,69,52,9,141,131,32,33,157,131, + 76,32,5,149,126,37,51,23,140,76,82,58,152,119,38,3,31,23,154,106,71,0,142,243,32,38,157,111,69,3,8,33,46,1,149,116,42,118,90,70,123,74,23,77,23,70,17,56,152,121,33,2,226,68,239,5,32, + 72,135,123,39,2,225,0,13,0,27,0,62,158,123,75,76,9,66,22,22,149,146,32,53,66,32,31,65,153,24,32,166,66,41,25,34,0,0,4,66,43,5,33,2,217,132,187,34,39,0,51,157,189,69,155,23,149,179, + 32,31,80,199,12,65,73,25,32,149,68,31,13,49,3,0,44,0,71,2,60,2,22,0,11,0,15,0,27,0,0,37,75,90,10,36,37,53,33,21,39,138,15,44,1,114,16,92,15,15,92,16,254,186,2,16,202,133,10,41,85,14, + 14,87,13,13,90,80,80,170,130,8,49,15,15,0,3,0,24,255,211,2,75,2,57,0,10,0,20,0,47,130,225,36,6,7,22,23,22,98,170,6,114,7,6,37,54,3,6,7,38,39,106,2,5,38,51,50,23,54,55,22,23,68,147, + 5,8,102,43,1,38,1,182,40,191,21,35,25,55,27,81,42,36,65,103,31,10,14,187,215,50,30,44,2,85,40,136,44,58,101,59,29,45,21,25,82,43,137,45,59,26,87,1,111,46,218,25,12,7,13,41,148,67,94, + 44,111,34,31,74,56,212,254,182,55,32,34,2,94,67,111,202,56,18,52,33,49,18,17,93,63,115,204,55,17,7,0,0,0,2,0,97,255,243,2,13,3,5,0,24,0,34,130,229,74,83,7,83,241,8,33,54,53,130,10, + 38,35,38,3,46,2,39,51,130,176,8,60,23,1,177,35,78,24,36,119,33,11,92,27,29,54,97,28,9,92,83,2,138,28,96,28,11,100,13,29,70,28,79,67,19,6,119,40,54,1,69,254,187,74,31,34,96,31,40,1, + 41,253,243,23,2,61,29,106,31,11,17,36,90,130,96,139,111,32,30,154,111,67,142,6,145,108,37,37,20,143,76,41,99,150,104,36,238,21,156,55,122,140,211,32,35,154,99,32,1,79,57,7,33,6,7,146, + 213,33,254,220,67,126,5,35,67,23,67,23,150,110,36,61,177,33,111,33,70,188,5,32,3,65,71,5,39,2,217,0,24,0,36,0,48,154,117,66,190,24,145,235,32,17,66,187,12,150,132,32,113,66,184,12, + 44,2,0,52,255,55,2,65,3,5,0,38,0,44,130,133,44,19,51,14,3,7,14,2,15,1,14,2,7,6,131,1,48,43,1,53,51,50,55,54,55,46,1,39,38,39,51,23,30,1,65,105,6,8,89,59,164,98,55,58,25,21,8,13,14, + 5,2,28,10,17,13,6,18,15,26,12,38,41,60,54,61,27,12,22,35,77,28,73,3,98,50,62,42,173,44,119,76,31,109,115,1,155,132,140,62,49,20,33,32,14,6,72,25,32,26,11,30,10,18,3,12,73,59,23,54, + 85,187,65,174,7,125,152,107,2,119,47,129,42,134,0,0,0,2,0,95,130,143,40,42,2,224,0,10,0,30,0,0,107,185,5,37,23,22,51,50,16,5,99,147,8,34,35,34,39,130,141,8,58,17,35,17,51,1,68,101, + 27,9,77,26,34,134,254,241,48,103,99,60,57,58,60,99,40,20,41,38,11,92,92,1,210,115,38,50,148,43,13,1,151,7,80,74,73,130,131,72,72,8,16,36,20,254,244,3,169,0,0,3,133,243,33,2,204,130, + 243,34,50,0,62,133,245,35,1,7,14,9,130,248,33,7,6,145,240,32,21,67,186,9,32,7,138,11,56,1,59,164,98,55,58,12,16,25,7,5,3,4,2,4,4,7,7,22,47,13,6,40,65,83,65,2,13,32,153,65,148,12,53, + 115,1,155,132,140,31,37,60,17,13,8,7,6,8,12,18,19,61,92,16,7,43,65,10,12,38,47,62,18,14,66,15,15,132,5,43,0,1,0,18,255,243,2,18,2,203,0,65,130,167,32,6,130,247,46,38,39,35,55,54,55, + 51,53,38,61,1,38,52,55,53,130,2,37,35,55,51,54,55,54,111,182,10,48,7,51,6,7,35,21,20,6,29,1,22,21,51,14,1,7,35,104,231,5,48,62,1,55,2,18,65,146,91,32,63,28,87,13,7,5,58,1,130,0,8,86, + 83,25,62,42,130,45,38,104,66,8,8,58,78,108,43,14,6,240,15,9,221,1,1,180,5,15,5,150,10,45,44,73,53,65,8,17,8,25,38,1,33,33,62,139,26,16,10,10,2,5,13,9,11,10,15,1,2,10,50,205,48,17,39, + 97,6,7,47,110,36,48,34,16,9,2,6,4,62,2,3,10,32,10,92,52,50,36,6,102,24,5,35,0,14,0,174,130,193,32,0,132,0,34,1,0,4,134,11,32,1,130,7,32,10,134,11,32,2,130,7,32,16,134,11,36,3,0,29, + 0,78,134,11,131,43,32,112,134,11,36,5,0,9,0,134,134,11,32,6,130,7,44,148,0,3,0,1,4,9,0,0,0,2,0,0,134,11,32,1,130,11,32,6,134,11,32,2,130,11,32,12,134,11,36,3,0,58,0,18,134,11,32,4, + 130,23,32,108,134,11,32,5,130,21,32,114,134,11,32,6,130,23,35,144,0,46,0,143,2,39,70,0,111,0,110,0,116,0,131,7,40,114,0,103,0,101,0,32,0,50,130,36,32,48,130,7,32,58,130,3,32,46,134, + 7,38,50,0,49,0,45,0,56,130,3,34,50,0,48,130,33,51,53,0,0,70,111,110,116,70,111,114,103,101,32,50,46,48,32,58,32,46,130,3,39,50,49,45,56,45,50,48,50,130,30,133,107,32,86,130,81,36,114, + 0,115,0,105,132,103,32,32,130,89,40,0,86,101,114,115,105,111,110,32,136,140,33,2,0,132,0,35,255,156,0,35,130,8,32,1,130,3,141,2,32,195,132,21,32,2,130,201,58,4,0,5,0,6,0,7,0,8,0,9, + 0,10,0,11,0,12,0,13,0,14,0,15,0,16,0,17,130,235,62,19,0,20,0,21,0,22,0,23,0,24,0,25,0,26,0,27,0,28,0,29,0,30,0,31,0,32,0,33,0,34,130,93,50,36,0,37,0,38,0,39,0,40,0,41,0,42,0,43,0,44, + 0,45,130,145,32,47,130,209,32,49,130,211,9,38,51,0,52,0,53,0,54,0,55,0,56,0,57,0,58,0,59,0,60,0,61,0,62,0,63,0,64,0,65,0,66,0,67,0,68,0,69,0,70,0,71,0,72,0,73,0,74,0,75,0,76,0,77,0, + 78,0,79,0,80,0,81,0,82,0,83,0,84,0,85,0,86,0,87,0,88,0,89,0,90,0,91,0,92,0,93,0,94,0,95,0,96,0,97,0,172,0,163,0,132,0,133,0,189,0,150,0,232,0,134,0,142,0,139,0,157,0,169,0,164,1,2, + 0,138,0,218,0,131,0,147,0,242,0,243,0,141,0,151,0,136,0,195,0,222,0,241,0,158,0,170,0,245,0,244,0,246,0,162,0,173,0,201,0,199,0,174,0,98,0,99,0,144,0,100,0,203,0,101,0,200,0,202,0, + 207,0,204,0,205,0,206,0,233,0,102,0,211,0,208,0,209,0,175,0,103,0,240,0,145,0,214,0,212,0,213,0,104,0,235,0,237,0,137,0,106,0,105,0,107,0,109,0,108,0,110,0,160,0,111,0,113,0,112,0, + 114,0,115,0,117,0,116,0,118,0,119,0,234,0,120,0,122,0,121,0,123,0,125,0,124,0,184,0,161,0,127,0,126,0,128,0,129,0,236,0,238,0,186,1,3,7,117,110,105,48,48,97,100,131,7,45,50,48,97,99, + 0,0,0,1,255,255,0,2,0,1,130,9,32,12,130,3,32,16,130,3,36,2,0,0,0,4,134,7,32,0,132,25,36,0,223,214,203,49,130,17,24,92,127,12,5,250,205,249,72,146, +}; + +static const char* GetDefaultCompressedFontDataProggyVector(int* out_size) +{ + *out_size = proggy_vector_minimal_ttf_compressed_size; + return (const char*)proggy_vector_minimal_ttf_compressed_data; +} + #endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT #endif // #ifndef IMGUI_DISABLE diff --git a/dep/imgui/src/imgui_freetype.cpp b/dep/imgui/src/imgui_freetype.cpp index 1955c0bb5..35a4e30b1 100644 --- a/dep/imgui/src/imgui_freetype.cpp +++ b/dep/imgui/src/imgui_freetype.cpp @@ -144,7 +144,7 @@ struct ImGui_ImplFreeType_Data struct ImGui_ImplFreeType_FontSrcData { // Initialize from an external data buffer. Doesn't copy data, and you must ensure it stays valid up to this object lifetime. - bool InitFont(FT_Library ft_library, ImFontConfig* src, ImGuiFreeTypeLoaderFlags extra_user_flags); + bool InitFont(FT_Library ft_library, const ImFontConfig* src, ImGuiFreeTypeLoaderFlags extra_user_flags); void CloseFont(); ImGui_ImplFreeType_FontSrcData() { memset((void*)this, 0, sizeof(*this)); } ~ImGui_ImplFreeType_FontSrcData() { CloseFont(); } @@ -168,9 +168,9 @@ struct ImGui_ImplFreeType_FontSrcBakedData ImGui_ImplFreeType_FontSrcBakedData() { memset((void*)this, 0, sizeof(*this)); } }; -bool ImGui_ImplFreeType_FontSrcData::InitFont(FT_Library ft_library, ImFontConfig* src, ImGuiFreeTypeLoaderFlags extra_font_loader_flags) +bool ImGui_ImplFreeType_FontSrcData::InitFont(FT_Library ft_library, const ImFontConfig* src, ImGuiFreeTypeLoaderFlags extra_font_loader_flags) { - FT_Error error = FT_New_Memory_Face(ft_library, (uint8_t*)src->FontData, (uint32_t)src->FontDataSize, (uint32_t)src->FontNo, &FtFace); + FT_Error error = FT_New_Memory_Face(ft_library, (const FT_Byte*)src->FontData, (FT_Long)src->FontDataSize, (FT_Long)src->FontNo, &FtFace); if (error != 0) return false; error = FT_Select_Charmap(FtFace, FT_ENCODING_UNICODE); @@ -208,12 +208,8 @@ bool ImGui_ImplFreeType_FontSrcData::InitFont(FT_Library ft_library, ImFontConfi LoadFlags = 0; if ((UserFlags & ImGuiFreeTypeLoaderFlags_Bitmap) == 0) LoadFlags |= FT_LOAD_NO_BITMAP; - if (UserFlags & ImGuiFreeTypeLoaderFlags_NoHinting) LoadFlags |= FT_LOAD_NO_HINTING; - else - src->PixelSnapH = true; // FIXME: A bit weird to do this this way. - if (UserFlags & ImGuiFreeTypeLoaderFlags_NoAutoHint) LoadFlags |= FT_LOAD_NO_AUTOHINT; if (UserFlags & ImGuiFreeTypeLoaderFlags_ForceAutoHint) @@ -373,7 +369,7 @@ static void* FreeType_Realloc(FT_Memory /*memory*/, long cur_size, long new_size return block; } -bool ImGui_ImplFreeType_LoaderInit(ImFontAtlas* atlas) +static bool ImGui_ImplFreeType_LoaderInit(ImFontAtlas* atlas) { IM_ASSERT(atlas->FontLoaderData == nullptr); ImGui_ImplFreeType_Data* bd = IM_NEW(ImGui_ImplFreeType_Data)(); @@ -406,7 +402,7 @@ bool ImGui_ImplFreeType_LoaderInit(ImFontAtlas* atlas) return true; } -void ImGui_ImplFreeType_LoaderShutdown(ImFontAtlas* atlas) +static void ImGui_ImplFreeType_LoaderShutdown(ImFontAtlas* atlas) { ImGui_ImplFreeType_Data* bd = (ImGui_ImplFreeType_Data*)atlas->FontLoaderData; IM_ASSERT(bd != nullptr); @@ -415,7 +411,7 @@ void ImGui_ImplFreeType_LoaderShutdown(ImFontAtlas* atlas) atlas->FontLoaderData = nullptr; } -bool ImGui_ImplFreeType_FontSrcInit(ImFontAtlas* atlas, ImFontConfig* src) +static bool ImGui_ImplFreeType_FontSrcInit(ImFontAtlas* atlas, ImFontConfig* src) { ImGui_ImplFreeType_Data* bd = (ImGui_ImplFreeType_Data*)atlas->FontLoaderData; ImGui_ImplFreeType_FontSrcData* bd_font_data = IM_NEW(ImGui_ImplFreeType_FontSrcData); @@ -432,7 +428,7 @@ bool ImGui_ImplFreeType_FontSrcInit(ImFontAtlas* atlas, ImFontConfig* src) return true; } -void ImGui_ImplFreeType_FontSrcDestroy(ImFontAtlas* atlas, ImFontConfig* src) +static void ImGui_ImplFreeType_FontSrcDestroy(ImFontAtlas* atlas, ImFontConfig* src) { IM_UNUSED(atlas); ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData; @@ -440,12 +436,13 @@ void ImGui_ImplFreeType_FontSrcDestroy(ImFontAtlas* atlas, ImFontConfig* src) src->FontLoaderData = nullptr; } -bool ImGui_ImplFreeType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src) +static bool ImGui_ImplFreeType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src) { IM_UNUSED(atlas); float size = baked->Size; if (src->MergeMode && src->SizePixels != 0.0f) - size *= (src->SizePixels / baked->ContainerFont->Sources[0]->SizePixels); + size *= (src->SizePixels / baked->OwnerFont->Sources[0]->SizePixels); + size *= src->ExtraSizeScale; ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData; @@ -503,7 +500,7 @@ bool ImGui_ImplFreeType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* src, ImF return true; } -void ImGui_ImplFreeType_FontBakedDestroy(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src) +static void ImGui_ImplFreeType_FontBakedDestroy(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src) { IM_UNUSED(atlas); IM_UNUSED(baked); @@ -516,7 +513,7 @@ void ImGui_ImplFreeType_FontBakedDestroy(ImFontAtlas* atlas, ImFontConfig* src, bd_baked_data->~ImGui_ImplFreeType_FontSrcBakedData(); // ~IM_PLACEMENT_DELETE() } -bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph) +static bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x) { ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData; uint32_t glyph_index = FT_Get_Char_Index(bd_font_data->FtFace, codepoint); @@ -554,9 +551,20 @@ bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src if (metrics == nullptr) return false; - // Render glyph into a bitmap (currently held by FreeType) FT_Face face = bd_font_data->FtFace; FT_GlyphSlot slot = face->glyph; + const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity; + + // Load metrics only mode + const float advance_x = (slot->advance.x / FT_SCALEFACTOR) / rasterizer_density; + if (out_advance_x != NULL) + { + IM_ASSERT(out_glyph == NULL); + *out_advance_x = advance_x; + return true; + } + + // Render glyph into a bitmap (currently held by FreeType) FT_Render_Mode render_mode = (bd_font_data->UserFlags & ImGuiFreeTypeLoaderFlags_Monochrome) ? FT_RENDER_MODE_MONO : FT_RENDER_MODE_NORMAL; error = FT_Render_Glyph(slot, render_mode); const FT_Bitmap* ft_bitmap = &slot->bitmap; @@ -566,11 +574,10 @@ bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src const int w = (int)ft_bitmap->width; const int h = (int)ft_bitmap->rows; const bool is_visible = (w != 0 && h != 0); - const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity; // Prepare glyph out_glyph->Codepoint = codepoint; - out_glyph->AdvanceX = (slot->advance.x / FT_SCALEFACTOR) / rasterizer_density; + out_glyph->AdvanceX = advance_x; // Pack and retrieve position inside texture atlas if (is_visible) @@ -589,14 +596,10 @@ bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src uint32_t* temp_buffer = (uint32_t*)atlas->Builder->TempBuffer.Data; ImGui_ImplFreeType_BlitGlyph(ft_bitmap, temp_buffer, w); - const float ref_size = baked->ContainerFont->Sources[0]->SizePixels; + const float ref_size = baked->OwnerFont->Sources[0]->SizePixels; const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f; - float font_off_x = (src->GlyphOffset.x * offsets_scale); - float font_off_y = (src->GlyphOffset.y * offsets_scale) + baked->Ascent; - if (src->PixelSnapH) // Snap scaled offset. This is to mitigate backward compatibility issues for GlyphOffset, but a better design would be welcome. - font_off_x = IM_ROUND(font_off_x); - if (src->PixelSnapV) - font_off_y = IM_ROUND(font_off_y); + float font_off_x = ImFloor(src->GlyphOffset.x * offsets_scale + 0.5f); // Snap scaled offset. + float font_off_y = ImFloor(src->GlyphOffset.y * offsets_scale + 0.5f) + baked->Ascent; float recip_h = 1.0f / rasterizer_density; float recip_v = 1.0f / rasterizer_density; @@ -616,7 +619,7 @@ bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src return true; } -bool ImGui_ImplFreetype_FontSrcContainsGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint) +static bool ImGui_ImplFreetype_FontSrcContainsGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint) { IM_UNUSED(atlas); ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData; diff --git a/dep/imgui/src/imgui_stdlib.cpp b/dep/imgui/src/imgui_stdlib.cpp index c04d487cc..282ca6234 100644 --- a/dep/imgui/src/imgui_stdlib.cpp +++ b/dep/imgui/src/imgui_stdlib.cpp @@ -1,10 +1,22 @@ // dear imgui: wrappers for C++ standard library (STL) types (std::string, etc.) + // This is also an example of how you may wrap your own similar types. +// TL;DR; this is using the ImGuiInputTextFlags_CallbackResize facility, +// which also demonstrated in 'Dear ImGui Demo->Widgets->Text Input->Resize Callback'. // Changelog: // - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string -// See more C++ related extension (fmt, RAII, syntaxis sugar) on Wiki: +// Usage: +// { +// #include "misc/cpp/imgui_stdlib.h" +// #include "misc/cpp/imgui_stdlib.cpp" // <-- If you want to include implementation without messing with your project/build. +// [...] +// std::string my_string; +// ImGui::InputText("my string", &my_string); +// } + +// See more C++ related extension (fmt, RAII, syntactic sugar) on Wiki: // https://github.com/ocornut/imgui/wiki/Useful-Extensions#cness #include "imgui.h" diff --git a/dep/imgui/src/imgui_tables.cpp b/dep/imgui/src/imgui_tables.cpp index 88b99d0a7..af97b2e96 100644 --- a/dep/imgui/src/imgui_tables.cpp +++ b/dep/imgui/src/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.0 +// dear imgui, v1.92.6 WIP // (tables and columns code) /* @@ -24,9 +24,9 @@ Index of this file: */ // Navigating this file: -// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. -// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments. -// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments. +// - In Visual Studio: Ctrl+Comma ("Edit.GoToAll") can follow symbols inside comments, whereas Ctrl+F12 ("Edit.GoToImplementation") cannot. +// - In Visual Studio w/ Visual Assist installed: Alt+G ("VAssistX.GoToImplementation") can also follow symbols inside comments. +// - In VS Code, CLion, etc.: Ctrl+Click can follow symbols inside comments. //----------------------------------------------------------------------------- // [SECTION] Commentary @@ -335,7 +335,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG // - always performing the GetOrAddByKey() O(log N) query in g.Tables.Map[]. const bool use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0; const ImVec2 avail_size = GetContentRegionAvail(); - const ImVec2 actual_outer_size = ImTrunc(CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f)); + const ImVec2 actual_outer_size = ImTrunc(CalcItemSize(outer_size, ImMax(avail_size.x, IMGUI_WINDOW_HARD_MIN_SIZE), use_child_window ? ImMax(avail_size.y, IMGUI_WINDOW_HARD_MIN_SIZE) : 0.0f)); const ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size); const bool outer_window_is_measuring_size = (outer_window->AutoFitFramesX > 0) || (outer_window->AutoFitFramesY > 0); // Doesn't apply to AlwaysAutoResize windows! if (use_child_window && IsClippedEx(outer_rect, 0) && !outer_window_is_measuring_size) @@ -437,7 +437,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG if (table->InnerWindow->SkipItems && outer_window_is_measuring_size) table->InnerWindow->SkipItems = false; - // When using multiple instances, ensure they have the same amount of horizontal decorations (aka vertical scrollbar) so stretched columns can be aligned) + // When using multiple instances, ensure they have the same amount of horizontal decorations (aka vertical scrollbar) so stretched columns can be aligned if (instance_no == 0) { table->HasScrollbarYPrev = table->HasScrollbarYCurr; @@ -451,7 +451,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG // But at this point we do NOT have a correct value for .Max.y (unless a height has been explicitly passed in). It will only be updated in EndTable(). table->WorkRect = table->OuterRect = table->InnerRect = outer_rect; table->HasScrollbarYPrev = table->HasScrollbarYCurr = false; - table->InnerWindow->DC.TreeDepth++; // This is designed to always linking ImGuiTreeNodeFlags_DrawLines linking accross a table + table->InnerWindow->DC.TreeDepth++; // This is designed to always linking ImGuiTreeNodeFlags_DrawLines linking across a table } // Push a standardized ID for both child-using and not-child-using tables @@ -464,6 +464,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->HostIndentX = inner_window->DC.Indent.x; table->HostClipRect = inner_window->ClipRect; table->HostSkipItems = inner_window->SkipItems; + temp_data->WindowID = inner_window->ID; temp_data->HostBackupWorkRect = inner_window->WorkRect; temp_data->HostBackupParentWorkRect = inner_window->ParentWorkRect; temp_data->HostBackupColumnsOffset = outer_window->DC.ColumnsOffset; @@ -542,7 +543,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG // Make table current g.CurrentTable = table; - outer_window->DC.NavIsScrollPushableX = false; // Shortcut for NavUpdateCurrentWindowIsScrollPushableX(); + inner_window->DC.NavIsScrollPushableX = false; // Shortcut for NavUpdateCurrentWindowIsScrollPushableX(); outer_window->DC.CurrentTableIdx = table_idx; if (inner_window != outer_window) // So EndChild() within the inner window can restore the table properly. inner_window->DC.CurrentTableIdx = table_idx; @@ -563,9 +564,9 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG const int old_columns_count = table->Columns.size(); if (old_columns_count != 0 && old_columns_count != columns_count) { - // Attempt to preserve width on column count change (#4046) + // 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; + old_columns_raw_data = table->RawData; // Free at end of function table->RawData = NULL; } if (table->RawData == NULL) @@ -591,7 +592,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG ImGuiTableColumn* column = &table->Columns[n]; if (old_columns_to_preserve && n < old_columns_count) { - // FIXME: We don't attempt to preserve column order in this path. *column = old_columns_to_preserve[n]; } else @@ -601,8 +601,9 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG column->WidthAuto = width_auto; column->IsPreserveWidthAuto = true; // Preserve WidthAuto when reinitializing a live table: not technically necessary but remove a visible flicker column->IsEnabled = column->IsUserEnabled = column->IsUserEnabledNextFrame = true; + column->DisplayOrder = (ImGuiTableColumnIdx)n; } - column->DisplayOrder = table->DisplayOrderToIndex[n] = (ImGuiTableColumnIdx)n; + table->DisplayOrderToIndex[n] = column->DisplayOrder; } } if (old_columns_raw_data) @@ -946,7 +947,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // (e.g. TextWrapped) too much. Otherwise what tends to happen is that TextWrapped would output a very // large height (= first frame scrollbar display very off + clipper would skip lots of items). // This is merely making the side-effect less extreme, but doesn't properly fixes it. - // FIXME: Move this to ->WidthGiven to avoid temporary lossyless? + // FIXME: Move this to ->WidthGiven to avoid temporary lossyness? // FIXME: This break IsPreserveWidthAuto from not flickering if the stored WidthAuto was smaller. if (column->AutoFitQueue > 0x01 && table->IsInitializing && !column->IsPreserveWidthAuto) column->WidthRequest = ImMax(column->WidthRequest, table->MinColumnWidth * 4.0f); // FIXME-TABLE: Another constant/scale? @@ -1190,7 +1191,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) } // In case the table is visible (e.g. decorations) but all columns clipped, we keep a column visible. - // Else if give no chance to a clipper-savy user to submit rows and therefore total contents height used by scrollbar. + // Else if give no chance to a clipper-savvy user to submit rows and therefore total contents height used by scrollbar. if (has_at_least_one_column_requesting_output == false) { table->Columns[table->LeftMostEnabledColumn].IsRequestOutput = true; @@ -1251,7 +1252,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // [Part 11] Default context menu // - To append to this menu: you can call TableBeginContextMenuPopup()/.../EndPopup(). - // - To modify or replace this: set table->IsContextPopupNoDefaultContents = true, then call TableBeginContextMenuPopup()/.../EndPopup(). + // - To modify or replace this: set table->DisableDefaultContextMenu = true, then call TableBeginContextMenuPopup()/.../EndPopup(). // - You may call TableDrawDefaultContextMenu() with selected flags to display specific sections of the default menu, // e.g. TableDrawDefaultContextMenu(table, table->Flags & ~ImGuiTableFlags_Hideable) will display everything EXCEPT columns visibility options. if (table->DisableDefaultContextMenu == false && TableBeginContextMenuPopup(table)) @@ -1366,7 +1367,7 @@ void ImGui::EndTable() ImGuiWindow* inner_window = table->InnerWindow; ImGuiWindow* outer_window = table->OuterWindow; ImGuiTableTempData* temp_data = table->TempData; - IM_ASSERT(inner_window == g.CurrentWindow); + IM_ASSERT(inner_window == g.CurrentWindow && inner_window->ID == temp_data->WindowID); IM_ASSERT(outer_window == inner_window || outer_window == inner_window->ParentWindow); if (table->IsInsideRow) @@ -1382,7 +1383,7 @@ void ImGui::EndTable() inner_window->DC.PrevLineSize = temp_data->HostBackupPrevLineSize; inner_window->DC.CurrLineSize = temp_data->HostBackupCurrLineSize; inner_window->DC.CursorMaxPos = temp_data->HostBackupCursorMaxPos; - const float inner_content_max_y = table->RowPosY2; + const float inner_content_max_y = ImCeil(table->RowPosY2); // Rounding final position is important as we currently don't round row height ('Demo->Tables->Outer Size' demo uses non-integer heights) IM_ASSERT(table->RowPosY2 == inner_window->DC.CursorPos.y); if (inner_window != outer_window) inner_window->DC.CursorMaxPos.y = inner_content_max_y; @@ -1559,7 +1560,7 @@ void ImGui::EndTable() IM_ASSERT(g.CurrentWindow == outer_window && g.CurrentTable == table); IM_ASSERT(g.TablesTempDataStacked > 0); temp_data = (--g.TablesTempDataStacked > 0) ? &g.TablesTempData[g.TablesTempDataStacked - 1] : NULL; - g.CurrentTable = temp_data ? g.Tables.GetByIndex(temp_data->TableIndex) : NULL; + g.CurrentTable = temp_data && (temp_data->WindowID == outer_window->ID) ? g.Tables.GetByIndex(temp_data->TableIndex) : NULL; if (g.CurrentTable) { g.CurrentTable->TempData = temp_data; @@ -1825,6 +1826,11 @@ void ImGui::TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; IM_ASSERT(target != ImGuiTableBgTarget_None); + if (table == NULL) + { + IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!"); + return; + } if (color == IM_COL32_DISABLE) color = 0; @@ -2049,10 +2055,11 @@ void ImGui::TableEndRow(ImGuiTable* table) } // End frozen rows (when we are past the last frozen row line, teleport cursor and alter clipping rectangle) - // We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark end of row and - // get the new cursor position. + // - We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark + // end of row and get the new cursor position. if (unfreeze_rows_request) { + IM_ASSERT(table->FreezeRowsRequest > 0); for (int column_n = 0; column_n < table->ColumnsCount; column_n++) table->Columns[column_n].NavLayerCurrent = table->NavLayer; const float y0 = ImMax(table->RowPosY2 + 1, table->InnerClipRect.Min.y); @@ -2451,6 +2458,11 @@ void ImGui::TableUpdateColumnsWeightFromWidth(ImGuiTable* table) // - TableDrawBorders() [Internal] //------------------------------------------------------------------------- + +// FIXME: This could be abstracted and merged with PushColumnsBackground(), by creating a generic struct +// with storage for backup cliprect + backup channel + storage for splitter pointer, new clip rect. +// This would slightly simplify caller code. + // Bg2 is used by Selectable (and possibly other widgets) to render to the background. // Unlike our Bg0/1 channel which we uses for RowBg/CellBg/Borders and where we guarantee all shapes to be CPU-clipped, the Bg2 channel being widgets-facing will rely on regular ClipRect. void ImGui::TablePushBackgroundChannel() @@ -2519,7 +2531,7 @@ void ImGui::TablePopColumnChannel() // - NoClip --> 2+D+1 channels: bg0/1 + bg2 + foreground (same clip rect == always 1 draw call) // - Clip --> 2+D+N channels // - FreezeRows --> 2+D+N*2 (unless scrolling value is zero) -// - FreezeRows || FreezeColunns --> 3+D+N*2 (unless scrolling value is zero) +// - FreezeRows || FreezeColumns --> 3+D+N*2 (unless scrolling value is zero) // Where D is 1 if any column is clipped or hidden (dummy channel) otherwise 0. void ImGui::TableSetupDrawChannels(ImGuiTable* table) { @@ -2612,7 +2624,7 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) const int size_for_masks_bitarrays_one = (int)ImBitArrayGetStorageSizeInBytes(max_draw_channels); g.TempBuffer.reserve(size_for_masks_bitarrays_one * 5); memset(g.TempBuffer.Data, 0, size_for_masks_bitarrays_one * 5); - for (int n = 0; n < IM_ARRAYSIZE(merge_groups); n++) + for (int n = 0; n < IM_COUNTOF(merge_groups); n++) merge_groups[n].ChannelsMask = (ImBitArrayPtr)(void*)(g.TempBuffer.Data + (size_for_masks_bitarrays_one * n)); ImBitArrayPtr remaining_mask = (ImBitArrayPtr)(void*)(g.TempBuffer.Data + (size_for_masks_bitarrays_one * 4)); @@ -2669,7 +2681,7 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) // [DEBUG] Display merge groups #if 0 if (g.IO.KeyShift) - for (int merge_group_n = 0; merge_group_n < IM_ARRAYSIZE(merge_groups); merge_group_n++) + for (int merge_group_n = 0; merge_group_n < IM_COUNTOF(merge_groups); merge_group_n++) { MergeGroup* merge_group = &merge_groups[merge_group_n]; if (merge_group->ChannelsCount == 0) @@ -2697,7 +2709,7 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) int remaining_count = splitter->_Count - (has_freeze_v ? LEADING_DRAW_CHANNELS + 1 : LEADING_DRAW_CHANNELS); //ImRect host_rect = (table->InnerWindow == table->OuterWindow) ? table->InnerClipRect : table->HostClipRect; ImRect host_rect = table->HostClipRect; - for (int merge_group_n = 0; merge_group_n < IM_ARRAYSIZE(merge_groups); merge_group_n++) + for (int merge_group_n = 0; merge_group_n < IM_COUNTOF(merge_groups); merge_group_n++) { if (int merge_channels_count = merge_groups[merge_group_n].ChannelsCount) { @@ -2812,8 +2824,13 @@ void ImGui::TableDrawBorders(ImGuiTable* table) continue; // Draw in outer window so right-most column won't be clipped - // Always draw full height border when being resized/hovered, or on the delimitation of frozen column scrolling. - float draw_y2 = (is_hovered || is_resized || is_frozen_separator || (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)) == 0) ? draw_y2_body : draw_y2_head; + float draw_y2 = draw_y2_head; + if (is_frozen_separator) + draw_y2 = draw_y2_body; + else if ((table->Flags & ImGuiTableFlags_NoBordersInBodyUntilResize) != 0 && (is_hovered || is_resized)) + draw_y2 = draw_y2_body; + else if ((table->Flags & (ImGuiTableFlags_NoBordersInBodyUntilResize | ImGuiTableFlags_NoBordersInBody)) == 0) + draw_y2 = draw_y2_body; if (draw_y2 > draw_y1) inner_drawlist->AddLine(ImVec2(column->MaxX, draw_y1), ImVec2(column->MaxX, draw_y2), TableGetColumnBorderCol(table, order_n, column_n), border_size); } @@ -2876,9 +2893,7 @@ ImGuiTableSortSpecs* ImGui::TableGetSortSpecs() { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; - IM_ASSERT(table != NULL); - - if (!(table->Flags & ImGuiTableFlags_Sortable)) + if (table == NULL || !(table->Flags & ImGuiTableFlags_Sortable)) return NULL; // Require layout (in case TableHeadersRow() hasn't been called) as it may alter IsSortSpecsDirty in some paths. @@ -3187,7 +3202,7 @@ void ImGui::TableHeader(const char* label) sort_arrow = true; if (column->SortOrder > 0) { - ImFormatString(sort_order_suf, IM_ARRAYSIZE(sort_order_suf), "%d", column->SortOrder + 1); + ImFormatString(sort_order_suf, IM_COUNTOF(sort_order_suf), "%d", column->SortOrder + 1); w_sort_text = g.Style.ItemInnerSpacing.x + CalcTextSize(sort_order_suf).x; } } @@ -3411,7 +3426,7 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label // Left<>Right alignment float line_off_curr_x = flip_label ? (label_lines - 1) * line_off_step_x : 0.0f; - float line_off_for_align_x = ImMax((((column->MaxX - column->MinX) - padding.x * 2.0f) - (label_lines * line_off_step_x)), 0.0f) * align.x; + float line_off_for_align_x = ImFloor(ImMax((((column->MaxX - column->MinX) - padding.x * 2.0f) - (label_lines * line_off_step_x)), 0.0f) * align.x); line_off_curr_x += line_off_for_align_x - line_off_for_ascent_x; // Register header width @@ -3777,7 +3792,6 @@ void ImGui::TableLoadSettings(ImGuiTable* table) // Serialize ImGuiTableSettings/ImGuiTableColumnSettings into ImGuiTable/ImGuiTableColumn ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings(); - ImU64 display_order_mask = 0; for (int data_n = 0; data_n < settings->ColumnsCount; data_n++, column_settings++) { int column_n = column_settings->Index; @@ -3794,24 +3808,51 @@ void ImGui::TableLoadSettings(ImGuiTable* table) } if (settings->SaveFlags & ImGuiTableFlags_Reorderable) column->DisplayOrder = column_settings->DisplayOrder; - display_order_mask |= (ImU64)1 << column->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; } - // Validate and fix invalid display order data - const ImU64 expected_display_order_mask = (settings->ColumnsCount == 64) ? ~0 : ((ImU64)1 << settings->ColumnsCount) - 1; - if (display_order_mask != expected_display_order_mask) - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - table->Columns[column_n].DisplayOrder = (ImGuiTableColumnIdx)column_n; - - // Rebuild index + // 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; } +struct ImGuiTableFixDisplayOrderColumnData +{ + ImGuiTableColumnIdx Idx; + ImGuiTable* Table; // This is unfortunate but we don't have userdata in qsort api. +}; + +// Sort by DisplayOrder and then Index +static int IMGUI_CDECL TableFixDisplayOrderComparer(const void* lhs, const void* rhs) +{ + 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); + return (order_delta > 0) ? +1 : (order_delta < 0) ? -1 : (lhs_idx > rhs_idx) ? +1 : -1; +} + +// Fix invalid display order data: compact values (0,1,3 -> 0,1,2); preserve relative order (0,3,1 -> 0,2,1); deduplicate (0,4,1,1 -> 0,3,1,2) +void ImGui::TableFixDisplayOrder(ImGuiTable* table) +{ + ImGuiContext& g = *GImGui; + g.TempBuffer.reserve((int)(sizeof(ImGuiTableFixDisplayOrderColumnData) * table->ColumnsCount)); // FIXME: Maybe wrap those two lines as a helper. + ImGuiTableFixDisplayOrderColumnData* fdo_columns = (ImGuiTableFixDisplayOrderColumnData*)(void*)g.TempBuffer.Data; + for (int n = 0; n < table->ColumnsCount; n++) + { + fdo_columns[n].Idx = (ImGuiTableColumnIdx)n; + fdo_columns[n].Table = 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; +} + static void TableSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*) { ImGuiContext& g = *ctx; @@ -3939,7 +3980,7 @@ void ImGui::TableSettingsAddSettingsHandler() // - TableGcCompactSettings() [Internal] //------------------------------------------------------------------------- -// Remove Table (currently only used by TestEngine) +// Remove Table data (currently only used by TestEngine) void ImGui::TableRemove(ImGuiTable* table) { //IMGUI_DEBUG_PRINT("TableRemove() id=0x%08X\n", table->ID); @@ -4018,9 +4059,9 @@ void ImGui::DebugNodeTable(ImGuiTable* table) bool open = TreeNode(table, "Table 0x%08X (%d columns, in '%s')%s", table->ID, table->ColumnsCount, table->OuterWindow->Name, is_active ? "" : " *Inactive*"); if (!is_active) { PopStyleColor(); } if (IsItemHovered()) - GetForegroundDrawList()->AddRect(table->OuterRect.Min, table->OuterRect.Max, IM_COL32(255, 255, 0, 255)); + GetForegroundDrawList(table->OuterWindow)->AddRect(table->OuterRect.Min, table->OuterRect.Max, IM_COL32(255, 255, 0, 255)); if (IsItemVisible() && table->HoveredColumnBody != -1) - GetForegroundDrawList()->AddRect(GetItemRectMin(), GetItemRectMax(), IM_COL32(255, 255, 0, 255)); + GetForegroundDrawList(table->OuterWindow)->AddRect(GetItemRectMin(), GetItemRectMax(), IM_COL32(255, 255, 0, 255)); if (!open) return; if (table->InstanceCurrent > 0) @@ -4053,7 +4094,7 @@ void ImGui::DebugNodeTable(ImGuiTable* table) ImGuiTableColumn* column = &table->Columns[n]; const char* name = TableGetColumnName(table, n); char buf[512]; - ImFormatString(buf, IM_ARRAYSIZE(buf), + ImFormatString(buf, IM_COUNTOF(buf), "Column %d order %d '%s': offset %+.2f to %+.2f%s\n" "Enabled: %d, VisibleX/Y: %d/%d, RequestOutput: %d, SkipItems: %d, DrawChannels: %d,%d\n" "WidthGiven: %.1f, Request/Auto: %.1f/%.1f, StretchWeight: %.3f (%.1f%%)\n" @@ -4074,7 +4115,7 @@ void ImGui::DebugNodeTable(ImGuiTable* table) if (IsItemHovered()) { ImRect r(column->MinX, table->OuterRect.Min.y, column->MaxX, table->OuterRect.Max.y); - GetForegroundDrawList()->AddRect(r.Min, r.Max, IM_COL32(255, 255, 0, 255)); + GetForegroundDrawList(table->OuterWindow)->AddRect(r.Min, r.Max, IM_COL32(255, 255, 0, 255)); } } if (ImGuiTableSettings* settings = TableGetBoundSettings(table)) diff --git a/dep/imgui/src/imgui_widgets.cpp b/dep/imgui/src/imgui_widgets.cpp index c77f38cac..adc416a3a 100644 --- a/dep/imgui/src/imgui_widgets.cpp +++ b/dep/imgui/src/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.0 +// dear imgui, v1.92.6 WIP // (widgets code) /* @@ -135,8 +135,7 @@ static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1); // For InputTextEx() static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard = false); -static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); -static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end, const char** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); +static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining = NULL, ImVec2* out_offset = NULL, ImDrawTextFlags flags = 0); //------------------------------------------------------------------------- // [SECTION] Widgets: Text, etc. @@ -573,9 +572,11 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool bool pressed = false; bool hovered = ItemHoverable(bb, id, item_flags); - // Special mode for Drag and Drop where holding button pressed for a long time while dragging another item triggers the button - if (g.DragDropActive && (flags & ImGuiButtonFlags_PressedOnDragDropHold) && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers)) - if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) + // Special mode for Drag and Drop used by openables (tree nodes, tabs etc.) + // where holding the button pressed for a long time while drag a payload item triggers the button. + if (g.DragDropActive) + { + if ((flags & ImGuiButtonFlags_PressedOnDragDropHold) && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) { hovered = true; SetHoveredID(id); @@ -586,6 +587,9 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool FocusWindow(window); } } + if (g.DragDropAcceptIdPrev == id && (g.DragDropAcceptFlagsPrev & ImGuiDragDropFlags_AcceptDrawAsHovered)) + hovered = true; + } if (flatten_hovered_children) g.HoveredWindow = backup_hovered_window; @@ -619,7 +623,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool if (flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere)) { SetActiveID(id, window); - g.ActiveIdMouseButton = mouse_button_clicked; + g.ActiveIdMouseButton = (ImS8)mouse_button_clicked; if (!(flags & ImGuiButtonFlags_NoNavFocus)) { SetFocusID(id, window); @@ -637,7 +641,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool ClearActiveID(); else SetActiveID(id, window); // Hold on ID - g.ActiveIdMouseButton = mouse_button_clicked; + g.ActiveIdMouseButton = (ImS8)mouse_button_clicked; if (!(flags & ImGuiButtonFlags_NoNavFocus)) { SetFocusID(id, window); @@ -675,32 +679,35 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool // Keyboard/Gamepad navigation handling // We report navigated and navigation-activated items as hovered but we don't set g.HoveredId to not interfere with mouse. - if (g.NavId == id && g.NavCursorVisible && g.NavHighlightItemUnderNav) - if (!(flags & ImGuiButtonFlags_NoHoveredOnFocus)) - hovered = true; - if (g.NavActivateDownId == id) + if ((item_flags & ImGuiItemFlags_Disabled) == 0) { - bool nav_activated_by_code = (g.NavActivateId == id); - bool nav_activated_by_inputs = (g.NavActivatePressedId == id); - if (!nav_activated_by_inputs && (item_flags & ImGuiItemFlags_ButtonRepeat)) - { - // Avoid pressing multiple keys from triggering excessive amount of repeat events - const ImGuiKeyData* key1 = GetKeyData(ImGuiKey_Space); - const ImGuiKeyData* key2 = GetKeyData(ImGuiKey_Enter); - const ImGuiKeyData* key3 = GetKeyData(ImGuiKey_NavGamepadActivate); - const float t1 = ImMax(ImMax(key1->DownDuration, key2->DownDuration), key3->DownDuration); - nav_activated_by_inputs = CalcTypematicRepeatAmount(t1 - g.IO.DeltaTime, t1, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0; - } - if (nav_activated_by_code || nav_activated_by_inputs) + if (g.NavId == id && g.NavCursorVisible && g.NavHighlightItemUnderNav) + if (!(flags & ImGuiButtonFlags_NoHoveredOnFocus)) + hovered = true; + if (g.NavActivateDownId == id) { - // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. - pressed = true; - SetActiveID(id, window); - g.ActiveIdSource = g.NavInputSource; - if (!(flags & ImGuiButtonFlags_NoNavFocus) && !(g.NavActivateFlags & ImGuiActivateFlags_FromShortcut)) - SetFocusID(id, window); - if (g.NavActivateFlags & ImGuiActivateFlags_FromShortcut) - g.ActiveIdFromShortcut = true; + bool nav_activated_by_code = (g.NavActivateId == id); + bool nav_activated_by_inputs = (g.NavActivatePressedId == id); + if (!nav_activated_by_inputs && (item_flags & ImGuiItemFlags_ButtonRepeat)) + { + // Avoid pressing multiple keys from triggering excessive amount of repeat events + const ImGuiKeyData* key1 = GetKeyData(ImGuiKey_Space); + const ImGuiKeyData* key2 = GetKeyData(ImGuiKey_Enter); + const ImGuiKeyData* key3 = GetKeyData(ImGuiKey_NavGamepadActivate); + const float t1 = ImMax(ImMax(key1->DownDuration, key2->DownDuration), key3->DownDuration); + nav_activated_by_inputs = CalcTypematicRepeatAmount(t1 - g.IO.DeltaTime, t1, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0; + } + if (nav_activated_by_code || nav_activated_by_inputs) + { + // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. + pressed = true; + SetActiveID(id, window); + g.ActiveIdSource = g.NavInputSource; + if (!(flags & ImGuiButtonFlags_NoNavFocus) && !(g.NavActivateFlags & ImGuiActivateFlags_FromShortcut)) + SetFocusID(id, window); + if (g.NavActivateFlags & ImGuiActivateFlags_FromShortcut) + g.ActiveIdFromShortcut = true; + } } } @@ -754,7 +761,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool } // Activation highlight (this may be a remote activation) - if (g.NavHighlightActivatedId == id) + if (g.NavHighlightActivatedId == id && (item_flags & ImGuiItemFlags_Disabled) == 0) hovered = true; if (out_hovered) *out_hovered = hovered; @@ -1025,10 +1032,13 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6 const bool allow_interaction = (alpha >= 1.0f); ImRect bb = bb_frame; - bb.Expand(ImVec2(-ImClamp(IM_TRUNC((bb_frame_width - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp(IM_TRUNC((bb_frame_height - 2.0f) * 0.5f), 0.0f, 3.0f))); + float padding = IM_TRUNC(ImMin(style.ScrollbarPadding, ImMin(bb_frame_width, bb_frame_height) * 0.5f)); + bb.Expand(-padding); // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar) const float scrollbar_size_v = (axis == ImGuiAxis_X) ? bb.GetWidth() : bb.GetHeight(); + if (scrollbar_size_v < 1.0f) + return false; // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount) // But we maintain a minimum size in pixel to allow for the user to still aim inside. @@ -1431,7 +1441,10 @@ void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* over // Render RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize)); - RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), fill_n0, fill_n1, style.FrameRounding); + float fill_x0 = ImLerp(bb.Min.x, bb.Max.x, fill_n0); + float fill_x1 = ImLerp(bb.Min.x, bb.Max.x, fill_n1); + if (fill_x0 < fill_x1) + RenderRectFilledInRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), fill_x0, fill_x1, style.FrameRounding); // Default displaying the fraction as percentage string, but user can override it // Don't display text for indeterminate bars by default @@ -1440,14 +1453,14 @@ void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* over { if (!overlay) { - ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction * 100 + 0.01f); + ImFormatString(overlay_buf, IM_COUNTOF(overlay_buf), "%.0f%%", fraction * 100 + 0.01f); overlay = overlay_buf; } ImVec2 overlay_size = CalcTextSize(overlay, NULL); if (overlay_size.x > 0.0f) { - float text_x = is_indeterminate ? (bb.Min.x + bb.Max.x - overlay_size.x) * 0.5f : ImLerp(bb.Min.x, bb.Max.x, fill_n1) + style.ItemSpacing.x; + float text_x = is_indeterminate ? (bb.Min.x + bb.Max.x - overlay_size.x) * 0.5f : fill_x1 + style.ItemSpacing.x; RenderTextClipped(ImVec2(ImClamp(text_x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f, 0.5f), &bb); } } @@ -1825,32 +1838,36 @@ static int IMGUI_CDECL ShrinkWidthItemComparer(const void* lhs, const void* rhs) const ImGuiShrinkWidthItem* b = (const ImGuiShrinkWidthItem*)rhs; if (int d = (int)(b->Width - a->Width)) return d; - return (b->Index - a->Index); + return b->Index - a->Index; } // Shrink excess width from a set of item, by removing width from the larger items first. // Set items Width to -1.0f to disable shrinking this item. -void ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess) +void ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess, float width_min) { if (count == 1) { if (items[0].Width >= 0.0f) - items[0].Width = ImMax(items[0].Width - width_excess, 1.0f); + items[0].Width = ImMax(items[0].Width - width_excess, width_min); return; } - ImQsort(items, (size_t)count, sizeof(ImGuiShrinkWidthItem), ShrinkWidthItemComparer); + ImQsort(items, (size_t)count, sizeof(ImGuiShrinkWidthItem), ShrinkWidthItemComparer); // Sort largest first, smallest last. int count_same_width = 1; - while (width_excess > 0.0f && count_same_width < count) + while (width_excess > 0.001f && count_same_width < count) { while (count_same_width < count && items[0].Width <= items[count_same_width].Width) count_same_width++; float max_width_to_remove_per_item = (count_same_width < count && items[count_same_width].Width >= 0.0f) ? (items[0].Width - items[count_same_width].Width) : (items[0].Width - 1.0f); + max_width_to_remove_per_item = ImMin(items[0].Width - width_min, max_width_to_remove_per_item); if (max_width_to_remove_per_item <= 0.0f) break; - float width_to_remove_per_item = ImMin(width_excess / count_same_width, max_width_to_remove_per_item); + float base_width_to_remove_per_item = ImMin(width_excess / count_same_width, max_width_to_remove_per_item); for (int item_n = 0; item_n < count_same_width; item_n++) - items[item_n].Width -= width_to_remove_per_item; - width_excess -= width_to_remove_per_item * count_same_width; + { + float width_to_remove_for_this_item = ImMin(base_width_to_remove_per_item, items[item_n].Width - width_min); + items[item_n].Width -= width_to_remove_for_this_item; + width_excess -= width_to_remove_for_this_item; + } } // Round width and redistribute remainder @@ -2003,7 +2020,7 @@ bool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags // This is essentially a specialized version of BeginPopupEx() char name[16]; - ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.BeginComboDepth); // Recycle windows based on depth + ImFormatString(name, IM_COUNTOF(name), "##Combo_%02d", g.BeginComboDepth); // Recycle windows based on depth // Set position given a custom constraint (peak into expected window size so we can position it) // FIXME: This might be easier to express with an hypothetical SetNextWindowPosConstraints() function? @@ -2027,7 +2044,8 @@ bool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags if (!ret) { EndPopup(); - IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above + if (!g.IO.ConfigDebugBeginReturnValueOnce && !g.IO.ConfigDebugBeginReturnValueLoop) // Begin may only return false with those debug tools activated. + IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above return false; } g.BeginComboDepth++; @@ -2223,6 +2241,11 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*old_getter)(void* // - RoundScalarWithFormat<>() //------------------------------------------------------------------------- +static const ImU32 GDefaultRgbaColorMarkers[4] = +{ + IM_COL32(240,20,20,255), IM_COL32(20,240,20,255), IM_COL32(20,20,240,255), IM_COL32(140,140,140,255) +}; + static const ImGuiDataTypeInfo GDataTypeInfo[] = { { sizeof(char), "S8", "%d", "%d" }, // ImGuiDataType_S8 @@ -2243,7 +2266,7 @@ static const ImGuiDataTypeInfo GDataTypeInfo[] = { sizeof(bool), "bool", "%d", "%d" }, // ImGuiDataType_Bool { 0, "char*","%s", "%s" }, // ImGuiDataType_String }; -IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT); +IM_STATIC_ASSERT(IM_COUNTOF(GDataTypeInfo) == ImGuiDataType_COUNT); const ImGuiDataTypeInfo* ImGui::DataTypeGetInfo(ImGuiDataType data_type) { @@ -2352,7 +2375,7 @@ bool ImGui::DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) format = type_info->ScanFmt; else - format = ImParseFormatSanitizeForScanning(format, format_sanitized, IM_ARRAYSIZE(format_sanitized)); + format = ImParseFormatSanitizeForScanning(format, format_sanitized, IM_COUNTOF(format_sanitized)); // Small types need a 32-bit buffer to receive the result from scanf() int v32 = 0; @@ -2443,7 +2466,7 @@ static float GetMinimumStepAtDecimalPrecision(int decimal_precision) static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f }; if (decimal_precision < 0) return FLT_MIN; - return (decimal_precision < IM_ARRAYSIZE(min_steps)) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision); + return (decimal_precision < IM_COUNTOF(min_steps)) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision); } template @@ -2457,12 +2480,12 @@ TYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, // Sanitize format char fmt_sanitized[32]; - ImParseFormatSanitizeForPrinting(fmt_start, fmt_sanitized, IM_ARRAYSIZE(fmt_sanitized)); + ImParseFormatSanitizeForPrinting(fmt_start, fmt_sanitized, IM_COUNTOF(fmt_sanitized)); fmt_start = fmt_sanitized; // Format value with our rounding, and read back char v_str[64]; - ImFormatString(v_str, IM_ARRAYSIZE(v_str), fmt_start, v); + ImFormatString(v_str, IM_COUNTOF(v_str), fmt_start, v); const char* p = v_str; while (*p == ' ') p++; @@ -2671,6 +2694,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); const float w = CalcItemWidth(); + const ImU32 color_marker = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasColorMarker) ? g.NextItemData.ColorMarker : 0; const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); @@ -2689,7 +2713,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); if (!temp_input_is_active) { - // Tabbing or CTRL+click on Drag turns it into an InputText + // Tabbing or Ctrl+Click on Drag turns it into an InputText const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id); const bool double_clicked = (hovered && g.IO.MouseClickedCount[0] == 2 && TestKeyOwner(ImGuiKey_MouseLeft, id)); const bool make_active = (clicked || double_clicked || g.NavActivateId == id); @@ -2723,7 +2747,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, if (temp_input_is_active) { - // Only clamp CTRL+Click input when ImGuiSliderFlags_ClampOnInput is set (generally via ImGuiSliderFlags_AlwaysClamp) + // Only clamp Ctrl+Click input when ImGuiSliderFlags_ClampOnInput is set (generally via ImGuiSliderFlags_AlwaysClamp) bool clamp_enabled = false; if ((flags & ImGuiSliderFlags_ClampOnInput) && (p_min != NULL || p_max != NULL)) { @@ -2739,7 +2763,10 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, // Draw frame const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); RenderNavCursor(frame_bb, id); - RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); + RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, false, style.FrameRounding); + if (color_marker != 0 && style.ColorMarkerSize > 0.0f) + RenderColorComponentMarker(frame_bb, GetColorU32(color_marker), style.FrameRounding); + RenderFrameBorder(frame_bb.Min, frame_bb.Max, g.Style.FrameRounding); // Drag behavior const bool value_changed = DragBehavior(id, data_type, p_data, v_speed, p_min, p_max, format, flags); @@ -2748,7 +2775,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. char value_buf[64]; - const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format); + const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_COUNTOF(value_buf), data_type, p_data, format); if (g.LogEnabled) LogSetNextTextDecoration("{", "}"); RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f)); @@ -2777,6 +2804,8 @@ bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* p_data PushID(i); if (i > 0) SameLine(0, g.Style.ItemInnerSpacing.x); + if (flags & ImGuiSliderFlags_ColorMarkers) + SetNextItemColorMarker(GDefaultRgbaColorMarkers[i]); value_changed |= DragScalar("", data_type, p_data, v_speed, p_min, p_max, format, flags); PopID(); PopItemWidth(); @@ -3275,6 +3304,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); const float w = CalcItemWidth(); + const ImU32 color_marker = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasColorMarker) ? g.NextItemData.ColorMarker : 0; const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); @@ -3293,7 +3323,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); if (!temp_input_is_active) { - // Tabbing or CTRL+click on Slider turns it into an input box + // Tabbing or Ctrl+Click on Slider turns it into an input box const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id); const bool make_active = (clicked || g.NavActivateId == id); if (make_active && clicked) @@ -3317,7 +3347,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat if (temp_input_is_active) { - // Only clamp CTRL+Click input when ImGuiSliderFlags_ClampOnInput is set (generally via ImGuiSliderFlags_AlwaysClamp) + // Only clamp Ctrl+Click input when ImGuiSliderFlags_ClampOnInput is set (generally via ImGuiSliderFlags_AlwaysClamp) const bool clamp_enabled = (flags & ImGuiSliderFlags_ClampOnInput) != 0; return TempInputScalar(frame_bb, id, label, data_type, p_data, format, clamp_enabled ? p_min : NULL, clamp_enabled ? p_max : NULL); } @@ -3325,7 +3355,10 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat // Draw frame const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); RenderNavCursor(frame_bb, id); - RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); + RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, false, style.FrameRounding); + if (color_marker != 0 && style.ColorMarkerSize > 0.0f) + RenderColorComponentMarker(frame_bb, GetColorU32(color_marker), style.FrameRounding); + RenderFrameBorder(frame_bb.Min, frame_bb.Max, g.Style.FrameRounding); // Slider behavior ImRect grab_bb; @@ -3339,7 +3372,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. char value_buf[64]; - const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format); + const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_COUNTOF(value_buf), data_type, p_data, format); if (g.LogEnabled) LogSetNextTextDecoration("{", "}"); RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f)); @@ -3369,6 +3402,8 @@ bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, i PushID(i); if (i > 0) SameLine(0, g.Style.ItemInnerSpacing.x); + if (flags & ImGuiSliderFlags_ColorMarkers) + SetNextItemColorMarker(GDefaultRgbaColorMarkers[i]); value_changed |= SliderScalar("", data_type, v, v_min, v_max, format, flags); PopID(); PopItemWidth(); @@ -3490,7 +3525,7 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. // For the vertical slider we allow centered text to overlap the frame padding char value_buf[64]; - const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format); + const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_COUNTOF(value_buf), data_type, p_data, format); RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.0f)); if (label_size.x > 0.0f) RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); @@ -3654,7 +3689,7 @@ int ImParseFormatPrecision(const char* fmt, int default_precision) return (precision == INT_MAX) ? default_precision : precision; } -// Create text input in place of another active widget (e.g. used when doing a CTRL+Click on drag/slider widgets) +// Create text input in place of another active widget (e.g. used when doing a Ctrl+Click on drag/slider widgets) // FIXME: Facilitate using this in variety of other situations. // FIXME: Among other things, setting ImGuiItemFlags_AllowDuplicateId in LastItemData is currently correct but // the expected relationship between TempInputXXX functions and LastItemData is a little fishy. @@ -3680,7 +3715,7 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* } // Note that Drag/Slider functions are only forwarding the min/max values clamping values if the ImGuiSliderFlags_AlwaysClamp flag is set! -// This is intended: this way we allow CTRL+Click manual input to set a value out of bounds, for maximum flexibility. +// This is intended: this way we allow Ctrl+Click manual input to set a value out of bounds, for maximum flexibility. // However this may not be ideal for all uses, as some user code may break on out of bound values. bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min, const void* p_clamp_max) { @@ -3690,16 +3725,16 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG const ImGuiDataTypeInfo* type_info = DataTypeGetInfo(data_type); char fmt_buf[32]; char data_buf[32]; - format = ImParseFormatTrimDecorations(format, fmt_buf, IM_ARRAYSIZE(fmt_buf)); + format = ImParseFormatTrimDecorations(format, fmt_buf, IM_COUNTOF(fmt_buf)); if (format[0] == 0) format = type_info->PrintFmt; - DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, p_data, format); + DataTypeFormatString(data_buf, IM_COUNTOF(data_buf), data_type, p_data, format); ImStrTrimBlanks(data_buf); 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. bool value_changed = false; - if (TempInputText(bb, id, label, data_buf, IM_ARRAYSIZE(data_buf), flags)) + if (TempInputText(bb, id, label, data_buf, IM_COUNTOF(data_buf), flags)) { // Backup old value size_t data_type_size = type_info->Size; @@ -3752,7 +3787,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data if ((flags & ImGuiInputTextFlags_DisplayEmptyRefVal) && DataTypeCompare(data_type, p_data, p_data_default) == 0) buf[0] = 0; else - DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format); + DataTypeFormatString(buf, IM_COUNTOF(buf), data_type, p_data, format); // Disable the MarkItemEdited() call in InputText but keep ImGuiItemStatusFlags_Edited. // We call MarkItemEdited() ourselves by comparing the actual data rather than the string. @@ -3762,7 +3797,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data bool value_changed = false; if (p_step == NULL) { - if (InputText(label, buf, IM_ARRAYSIZE(buf), flags)) + if (InputText(label, buf, IM_COUNTOF(buf), flags)) value_changed = DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL); } else @@ -3772,7 +3807,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data BeginGroup(); // The only purpose of the group here is to allow the caller to query item data e.g. IsItemActive() PushID(label); SetNextItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2)); - if (InputText("", buf, IM_ARRAYSIZE(buf), flags)) // PushId(label) + "" gives us the expected ID from outside point of view + if (InputText("", buf, IM_COUNTOF(buf), flags)) // PushId(label) + "" gives us the expected ID from outside point of view value_changed = DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL); IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Inputable); @@ -3906,9 +3941,6 @@ bool ImGui::InputDouble(const char* label, double* v, double step, double step_f // - InputText() // - InputTextWithHint() // - InputTextMultiline() -// - InputTextGetCharInfo() [Internal] -// - InputTextReindexLines() [Internal] -// - InputTextReindexLinesRange() [Internal] // - InputTextEx() [Internal] // - DebugNodeInputTextState() [Internal] //------------------------------------------------------------------------- @@ -3918,6 +3950,7 @@ namespace ImStb #include "imstb_textedit.h" } +// If you want to use InputText() with std::string or any custom dynamic string type, use the wrapper in misc/cpp/imgui_stdlib.h/.cpp! bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) { IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() @@ -3935,75 +3968,12 @@ bool ImGui::InputTextWithHint(const char* label, const char* hint, char* buf, si return InputTextEx(label, hint, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data); } -// This is only used in the path where the multiline widget is inactive. -static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end) -{ - int line_count = 0; - const char* s = text_begin; - while (true) - { - const char* s_eol = strchr(s, '\n'); - line_count++; - if (s_eol == NULL) - { - s = s + ImStrlen(s); - break; - } - s = s_eol + 1; - } - *out_text_end = s; - return line_count; -} - -// FIXME: Ideally we'd share code with ImFont::CalcTextSizeA() -static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end, const char** remaining, ImVec2* out_offset, bool stop_on_new_line) +static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining, ImVec2* out_offset, ImDrawTextFlags flags) { ImGuiContext& g = *ctx; - //ImFont* font = g.Font; - ImFontBaked* baked = g.FontBaked; - const float line_height = g.FontSize; - const float scale = line_height / baked->Size; - - ImVec2 text_size = ImVec2(0, 0); - float line_width = 0.0f; - - const char* s = text_begin; - while (s < text_end) - { - unsigned int c = (unsigned int)*s; - if (c < 0x80) - s += 1; - else - s += ImTextCharFromUtf8(&c, s, text_end); - - if (c == '\n') - { - text_size.x = ImMax(text_size.x, line_width); - text_size.y += line_height; - line_width = 0.0f; - if (stop_on_new_line) - break; - continue; - } - if (c == '\r') - continue; - - line_width += baked->GetCharAdvance((ImWchar)c) * scale; - } - - if (text_size.x < line_width) - text_size.x = line_width; - - if (out_offset) - *out_offset = ImVec2(line_width, text_size.y + line_height); // offset allow for the possibility of sitting after a trailing \n - - if (line_width > 0 || text_size.y == 0.0f) // whereas size.y will ignore the trailing \n - text_size.y += line_height; - - if (remaining) - *remaining = s; - - return text_size; + ImGuiInputTextState* obj = &g.InputTextState; + IM_ASSERT(text_end_display >= text_begin && text_end_display <= text_end); + return ImFontCalcTextSizeEx(g.Font, g.FontSize, g.FontWeight, FLT_MAX, obj->WrapWidth, text_begin, text_end_display, text_end, out_remaining, out_offset, flags); } // Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar) @@ -4014,14 +3984,14 @@ static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, c namespace ImStb { static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->TextLen; } -static char STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { IM_ASSERT(idx <= obj->TextLen); return obj->TextSrc[idx]; } +static char STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { IM_ASSERT(idx >= 0 && idx <= obj->TextLen); return obj->TextSrc[idx]; } static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { unsigned int c; ImTextCharFromUtf8(&c, obj->TextSrc + line_start_idx + char_idx, obj->TextSrc + obj->TextLen); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.FontBaked->GetCharAdvance((ImWchar)c) * g.FontBakedScale; } static char STB_TEXTEDIT_NEWLINE = '\n'; static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx) { const char* text = obj->TextSrc; const char* text_remaining = NULL; - const ImVec2 size = InputTextCalcTextSize(obj->Ctx, text + line_start_idx, text + obj->TextLen, &text_remaining, NULL, true); + const ImVec2 size = InputTextCalcTextSize(obj->Ctx, text + line_start_idx, text + obj->TextLen, text + obj->TextLen, &text_remaining, NULL, ImDrawTextFlags_StopOnNewLine | ImDrawTextFlags_WrapKeepBlanks); r->x0 = 0.0f; r->x1 = size.x; r->baseline_y_delta = size.y; @@ -4065,7 +4035,7 @@ static bool ImCharIsSeparatorW(unsigned int c) static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx) { - // When ImGuiInputTextFlags_Password is set, we don't want actions such as CTRL+Arrow to leak the fact that underlying data are blanks or separators. + // When ImGuiInputTextFlags_Password is set, we don't want actions such as Ctrl+Arrow to leak the fact that underlying data are blanks or separators. if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0) return 0; @@ -4123,6 +4093,75 @@ static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx) #define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h #define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL +// Reimplementation of stb_textedit_move_line_start()/stb_textedit_move_line_end() which supports word-wrapping. +static int STB_TEXTEDIT_MOVELINESTART_IMPL(ImGuiInputTextState* obj, ImStb::STB_TexteditState* state, int cursor) +{ + if (state->single_line) + return 0; + + if (obj->WrapWidth > 0.0f) + { + ImGuiContext& g = *obj->Ctx; + const char* p_cursor = obj->TextSrc + cursor; + const char* p_bol = ImStrbol(p_cursor, obj->TextSrc); + const char* p = p_bol; + const char* text_end = obj->TextSrc + obj->TextLen; // End of line would be enough + while (p >= p_bol) + { + const char* p_eol = ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, g.FontWeight, p, text_end, obj->WrapWidth, ImDrawTextFlags_WrapKeepBlanks); + if (p == p_cursor) // If we are already on a visible beginning-of-line, return real beginning-of-line (would be same as regular handler below) + return (int)(p_bol - obj->TextSrc); + if (p_eol == p_cursor && obj->TextA[cursor] != '\n' && obj->LastMoveDirectionLR == ImGuiDir_Left) + return (int)(p_bol - obj->TextSrc); + if (p_eol >= p_cursor) + return (int)(p - obj->TextSrc); + p = (*p_eol == '\n') ? p_eol + 1 : p_eol; + } + } + + // Regular handler, same as stb_textedit_move_line_start() + while (cursor > 0) + { + int prev_cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, cursor); + if (STB_TEXTEDIT_GETCHAR(obj, prev_cursor) == STB_TEXTEDIT_NEWLINE) + break; + cursor = prev_cursor; + } + return cursor; +} + +static int STB_TEXTEDIT_MOVELINEEND_IMPL(ImGuiInputTextState* obj, ImStb::STB_TexteditState* state, int cursor) +{ + int n = STB_TEXTEDIT_STRINGLEN(obj); + if (state->single_line) + return n; + + if (obj->WrapWidth > 0.0f) + { + ImGuiContext& g = *obj->Ctx; + const char* p_cursor = obj->TextSrc + cursor; + const char* p = ImStrbol(p_cursor, obj->TextSrc); + const char* text_end = obj->TextSrc + obj->TextLen; // End of line would be enough + while (p < text_end) + { + const char* p_eol = ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, g.FontWeight, p, text_end, obj->WrapWidth, ImDrawTextFlags_WrapKeepBlanks); + cursor = (int)(p_eol - obj->TextSrc); + if (p_eol == p_cursor && obj->LastMoveDirectionLR != ImGuiDir_Left) // If we are already on a visible end-of-line, switch to regular handle + break; + if (p_eol > p_cursor) + return cursor; + p = (*p_eol == '\n') ? p_eol + 1 : p_eol; + } + } + // Regular handler, same as stb_textedit_move_line_end() + while (cursor < n && STB_TEXTEDIT_GETCHAR(obj, cursor) != STB_TEXTEDIT_NEWLINE) + cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, cursor); + return cursor; +} + +#define STB_TEXTEDIT_MOVELINESTART STB_TEXTEDIT_MOVELINESTART_IMPL +#define STB_TEXTEDIT_MOVELINEEND STB_TEXTEDIT_MOVELINEEND_IMPL + static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n) { // Offset remaining text (+ copy zero terminator) @@ -4134,21 +4173,23 @@ static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n) obj->TextLen -= n; } -static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const char* new_text, int new_text_len) +static int STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const char* new_text, int new_text_len) { const bool is_resizable = (obj->Flags & ImGuiInputTextFlags_CallbackResize) != 0; const int text_len = obj->TextLen; IM_ASSERT(pos <= text_len); - if (!is_resizable && (new_text_len + obj->TextLen + 1 > obj->BufCapacity)) - return false; + // We support partial insertion (with a mod in stb_textedit.h) + const int avail = obj->BufCapacity - 1 - obj->TextLen; + if (!is_resizable && new_text_len > avail) + new_text_len = (int)(ImTextFindValidUtf8CodepointEnd(new_text, new_text + new_text_len, new_text + avail) - new_text); // Truncate to closest UTF-8 codepoint. Alternative: return 0 to cancel insertion. + if (new_text_len == 0) + return 0; // Grow internal buffer if needed IM_ASSERT(obj->TextSrc == obj->TextA.Data); - if (new_text_len + text_len + 1 > obj->TextA.Size) + if (text_len + new_text_len + 1 > obj->TextA.Size && is_resizable) { - if (!is_resizable) - return false; obj->TextA.resize(text_len + ImClamp(new_text_len, 32, ImMax(256, new_text_len)) + 1); obj->TextSrc = obj->TextA.Data; } @@ -4162,7 +4203,7 @@ static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const ch obj->TextLen += new_text_len; obj->TextA[obj->TextLen] = '\0'; - return true; + return new_text_len; } // We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols) @@ -4197,7 +4238,8 @@ static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* st state->cursor = state->select_start = state->select_end = 0; if (text_len <= 0) return; - if (ImStb::STB_TEXTEDIT_INSERTCHARS(str, 0, text, text_len)) + int text_len_inserted = ImStb::STB_TEXTEDIT_INSERTCHARS(str, 0, text, text_len); + if (text_len_inserted > 0) { state->cursor = state->select_start = state->select_end = text_len; state->has_preferred_x = 0; @@ -4226,6 +4268,11 @@ void ImGuiInputTextState::OnKeyPressed(int key) stb_textedit_key(this, Stb, key); CursorFollow = true; CursorAnimReset(); + const int key_u = (key & ~STB_TEXTEDIT_K_SHIFT); + if (key_u == STB_TEXTEDIT_K_LEFT || key_u == STB_TEXTEDIT_K_LINESTART || key_u == STB_TEXTEDIT_K_TEXTSTART || key_u == STB_TEXTEDIT_K_BACKSPACE || key_u == STB_TEXTEDIT_K_WORDLEFT) + LastMoveDirectionLR = ImGuiDir_Left; + else if (key_u == STB_TEXTEDIT_K_RIGHT || key_u == STB_TEXTEDIT_K_LINEEND || key_u == STB_TEXTEDIT_K_TEXTEND || key_u == STB_TEXTEDIT_K_DELETE || key_u == STB_TEXTEDIT_K_WORDRIGHT) + LastMoveDirectionLR = ImGuiDir_Right; } void ImGuiInputTextState::OnCharPressed(unsigned int c) @@ -4247,6 +4294,7 @@ void ImGuiInputTextState::ClearSelection() { Stb->select_start int ImGuiInputTextState::GetCursorPos() const { return Stb->cursor; } int ImGuiInputTextState::GetSelectionStart() const { return Stb->select_start; } int ImGuiInputTextState::GetSelectionEnd() const { return Stb->select_end; } +float ImGuiInputTextState::GetPreferredOffsetX() const { return Stb->has_preferred_x ? Stb->preferred_x : -1; } void ImGuiInputTextState::SelectAll() { Stb->select_start = 0; Stb->cursor = Stb->select_end = TextLen; Stb->has_preferred_x = 0; } void ImGuiInputTextState::ReloadUserBufAndSelectAll() { WantReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; } void ImGuiInputTextState::ReloadUserBufAndKeepSelection() { WantReloadUserBuf = true; ReloadSelectionStart = Stb->select_start; ReloadSelectionEnd = Stb->select_end; } @@ -4286,15 +4334,20 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons ImGuiContext& g = *Ctx; ImGuiInputTextState* obj = &g.InputTextState; IM_ASSERT(obj->ID != 0 && g.ActiveId == obj->ID); + const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0; + const bool is_readonly = (Flags & ImGuiInputTextFlags_ReadOnly) != 0; + int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)ImStrlen(new_text); + + // We support partial insertion (with a mod in stb_textedit.h) + const int avail = BufSize - 1 - BufTextLen; + if (!is_resizable && new_text_len > avail) + new_text_len = (int)(ImTextFindValidUtf8CodepointEnd(new_text, new_text + new_text_len, new_text + avail) - new_text); // Truncate to closest UTF-8 codepoint. Alternative: return 0 to cancel insertion. + if (new_text_len == 0) + return; // Grow internal buffer if needed - const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0; - const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)ImStrlen(new_text); - if (new_text_len + BufTextLen + 1 > obj->TextA.Size && (Flags & ImGuiInputTextFlags_ReadOnly) == 0) + if (new_text_len + BufTextLen + 1 > obj->TextA.Size && is_resizable && !is_readonly) { - if (!is_resizable) - return; - IM_ASSERT(Buf == obj->TextA.Data); int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1; obj->TextA.resize(new_buf_size + 1); @@ -4308,11 +4361,12 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char)); Buf[BufTextLen + new_text_len] = '\0'; + BufDirty = true; + BufTextLen += new_text_len; if (CursorPos >= pos) CursorPos += new_text_len; + CursorPos = ImClamp(CursorPos, 0, BufTextLen); SelectionStart = SelectionEnd = CursorPos; - BufDirty = true; - BufTextLen += new_text_len; } void ImGui::PushPasswordFont() @@ -4396,7 +4450,7 @@ static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, Im if (c == '.' || c == ',') c = c_decimal_point; - // Full-width -> half-width conversion for numeric fields (https://en.wikipedia.org/wiki/Halfwidth_and_Fullwidth_Forms_(Unicode_block) + // Full-width -> half-width conversion for numeric fields: https://en.wikipedia.org/wiki/Halfwidth_and_Fullwidth_Forms_(Unicode_block) // While this is mostly convenient, this has the side-effect for uninformed users accidentally inputting full-width characters that they may // scratch their head as to why it works in numerical fields vs in generic text fields it would require support in the font. if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsScientific | ImGuiInputTextFlags_CharsHexadecimal)) @@ -4501,14 +4555,103 @@ void ImGui::InputTextDeactivateHook(ImGuiID id) } } +static int* ImLowerBound(int* in_begin, int* in_end, int v) +{ + int* in_p = in_begin; + for (size_t count = (size_t)(in_end - in_p); count > 0; ) + { + size_t count2 = count >> 1; + int* mid = in_p + count2; + if (*mid < v) + { + in_p = ++mid; + count -= count2 + 1; + } + else + { + count = count2; + } + } + return in_p; +} + +// FIXME-WORDWRAP: Bundle some of this into ImGuiTextIndex and/or extract as a different tool? +// 'max_output_buffer_size' happens to be a meaningful optimization to avoid writing the full line_index when not necessarily needed (e.g. very large buffer, scrolled up, inactive) +static int InputTextLineIndexBuild(ImGuiInputTextFlags flags, ImGuiTextIndex* line_index, const char* buf, const char* buf_end, float wrap_width, int max_output_buffer_size, const char** out_buf_end) +{ + ImGuiContext& g = *GImGui; + int size = 0; + const char* s; + if (flags & ImGuiInputTextFlags_WordWrap) + { + for (s = buf; s < buf_end; s = (*s == '\n') ? s + 1 : s) + { + if (size++ <= max_output_buffer_size) + line_index->Offsets.push_back((int)(s - buf)); + s = ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, g.FontWeight, s, buf_end, wrap_width, ImDrawTextFlags_WrapKeepBlanks); + } + } + else if (buf_end != NULL) + { + for (s = buf; s < buf_end; s = s ? s + 1 : buf_end) + { + if (size++ <= max_output_buffer_size) + line_index->Offsets.push_back((int)(s - buf)); + s = (const char*)ImMemchr(s, '\n', buf_end - s); + } + } + else + { + const char* s_eol; + for (s = buf; ; s = s_eol + 1) + { + if (size++ <= max_output_buffer_size) + line_index->Offsets.push_back((int)(s - buf)); + if ((s_eol = strchr(s, '\n')) != NULL) + continue; + s += strlen(s); + break; + } + } + if (out_buf_end != NULL) + *out_buf_end = buf_end = s; + if (size == 0) + { + line_index->Offsets.push_back(0); + size++; + } + if (buf_end > buf && buf_end[-1] == '\n' && size <= max_output_buffer_size) + { + line_index->Offsets.push_back((int)(buf_end - buf)); + size++; + } + return size; +} + +static ImVec2 InputTextLineIndexGetPosOffset(ImGuiContext& g, ImGuiInputTextState* state, ImGuiTextIndex* line_index, const char* buf, const char* buf_end, int cursor_n) +{ + const char* cursor_ptr = buf + cursor_n; + int* it_begin = line_index->Offsets.begin(); + int* it_end = line_index->Offsets.end(); + const int* it = ImLowerBound(it_begin, it_end, cursor_n); + if (it > it_begin) + if (it == it_end || *it != cursor_n || (state != NULL && state->WrapWidth > 0.0f && state->LastMoveDirectionLR == ImGuiDir_Right && cursor_ptr[-1] != '\n' && cursor_ptr[-1] != 0)) + it--; + + const int line_no = (it == it_begin) ? 0 : line_index->Offsets.index_from_ptr(it); + const char* line_start = line_index->get_line_begin(buf, line_no); + ImVec2 offset; + offset.x = InputTextCalcTextSize(&g, line_start, cursor_ptr, buf_end, NULL, NULL, ImDrawTextFlags_WrapKeepBlanks).x; + offset.y = (line_no + 1) * g.FontSize; + return offset; +} + // Edit a string of text // - buf_size account for the zero-terminator, so a buf_size of 6 can hold "Hello" but not "Hello!". // This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match // Note that in std::string world, capacity() would omit 1 byte used by the zero-terminator. // - When active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while the InputText is active has no effect. -// - If you want to use ImGui::InputText() with std::string, see misc/cpp/imgui_stdlib.h -// (FIXME: Rather confusing and messy function, among the worse part of our codebase, expecting to rewrite a V2 at some point.. Partly because we are -// doing UTF8 > U16 > UTF8 conversions on the go to easily interface with stb_textedit. Ideally should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188) +// - If you want to use InputText() with std::string or any custom dynamic string type, use the wrapper in misc/cpp/imgui_stdlib.h/.cpp! bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* callback_user_data) { ImGuiWindow* window = GetCurrentWindow(); @@ -4518,7 +4661,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ IM_ASSERT(buf != NULL && buf_size >= 0); IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys) IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key) - IM_ASSERT(!((flags & ImGuiInputTextFlags_ElideLeft) && (flags & ImGuiInputTextFlags_Multiline))); // Multiline will not work with left-trimming + IM_ASSERT(!((flags & ImGuiInputTextFlags_ElideLeft) && (flags & ImGuiInputTextFlags_Multiline))); // Multiline does not not work with left-trimming + IM_ASSERT((flags & ImGuiInputTextFlags_WordWrap) == 0 || (flags & ImGuiInputTextFlags_Password) == 0); // WordWrap does not work with Password mode. + IM_ASSERT((flags & ImGuiInputTextFlags_WordWrap) == 0 || (flags & ImGuiInputTextFlags_Multiline) != 0); // WordWrap does not work in single-line mode. ImGuiContext& g = *GImGui; ImGuiIO& io = g.IO; @@ -4552,8 +4697,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ item_data_backup = g.LastItemData; window->DC.CursorPos = backup_pos; - // Prevent NavActivation from Tabbing when our widget accepts Tab inputs: this allows cycling through widgets without stopping. - if (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_FromTabbing) && (flags & ImGuiInputTextFlags_AllowTabInput)) + // Prevent NavActivation from explicit Tabbing when our widget accepts Tab inputs: this allows cycling through widgets without stopping. + if (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_FromTabbing) && !(g.NavActivateFlags & ImGuiActivateFlags_FromFocusApi) && (flags & ImGuiInputTextFlags_AllowTabInput)) g.NavActivateId = 0; // Prevent NavActivate reactivating in BeginChild() when we are already active. @@ -4609,6 +4754,13 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (is_resizable) IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag! + // Word-wrapping: enforcing a fixed width not altered by vertical scrollbar makes things easier, notably to track cursor reliably and avoid one-frame glitches. + // Instead of using ImGuiWindowFlags_AlwaysVerticalScrollbar we account for that space if the scrollbar is not visible. + const bool is_wordwrap = (flags & ImGuiInputTextFlags_WordWrap) != 0; + float wrap_width = 0.0f; + if (is_wordwrap) + wrap_width = ImMax(1.0f, GetContentRegionAvail().x + (draw_window->ScrollbarY ? 0.0f : -g.Style.ScrollbarSize)); + const bool input_requested_by_nav = (g.ActiveId != id) && ((g.NavActivateId == id) && ((g.NavActivateFlags & ImGuiActivateFlags_PreferInput) || (g.NavInputSource == ImGuiInputSource_Keyboard || g.NavInputSource == ImGuiInputSource_Gamepad))); const bool user_clicked = hovered && io.MouseClicked[0]; @@ -4633,8 +4785,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ state->TextLen = new_len; memcpy(state->TextA.Data, buf, state->TextLen + 1); state->Stb->select_start = state->ReloadSelectionStart; - state->Stb->cursor = state->Stb->select_end = state->ReloadSelectionEnd; - state->CursorClamp(); + state->Stb->cursor = state->Stb->select_end = state->ReloadSelectionEnd; // will be clamped to bounds below } else if ((init_state && g.ActiveId != id) || init_changed_specs) { @@ -4648,7 +4799,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // 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) const int buf_len = (int)ImStrlen(buf); - IM_ASSERT(buf_len + 1 <= buf_size && "Is your input buffer properly zero-terminated?"); + IM_ASSERT(((buf_len + 1 <= buf_size) || (buf_len == 0 && buf_size == 0)) && "Is your input buffer properly zero-terminated?"); state->TextToRevertTo.resize(buf_len + 1); // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string. memcpy(state->TextToRevertTo.Data, buf, buf_len + 1); @@ -4674,9 +4825,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Recycle existing cursor/selection/undo stack but clamp position // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler. - if (recycle_state) - state->CursorClamp(); - else + if (!recycle_state) stb_textedit_initialize_state(state->Stb, !is_multiline); if (!is_multiline) @@ -4705,7 +4854,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { // Declare some inputs, the other are registered and polled via Shortcut() routing system. // FIXME: The reason we don't use Shortcut() is we would need a routing flag to specify multiple mods, or to all mods combination into individual shortcuts. - const ImGuiKey always_owned_keys[] = { ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_Enter, ImGuiKey_KeypadEnter, ImGuiKey_Delete, ImGuiKey_Backspace, ImGuiKey_Home, ImGuiKey_End }; + const ImGuiKey always_owned_keys[] = { ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_Delete, ImGuiKey_Backspace, ImGuiKey_Home, ImGuiKey_End }; for (ImGuiKey key : always_owned_keys) SetKeyOwner(key, id); if (user_clicked) @@ -4733,6 +4882,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Read-only mode always ever read from source buffer. Refresh TextLen when active. if (is_readonly && state != NULL) state->TextLen = (int)ImStrlen(buf); + if (state != NULL) + state->CursorClamp(); //if (is_readonly && state != NULL) // state->TextA.clear(); // Uncomment to facilitate debugging, but we otherwise prefer to keep/amortize th allocation. } @@ -4761,6 +4912,15 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (is_password && !is_displaying_hint) PushPasswordFont(); + // Word-wrapping: attempt to keep cursor in view while resizing frame/parent + // FIXME-WORDWRAP: It would be better to preserve same relative offset. + if (is_wordwrap && state != NULL && state->ID == id && state->WrapWidth != wrap_width) + { + state->CursorCenterY = true; + state->WrapWidth = wrap_width; + render_cursor = true; + } + // Process mouse inputs and character inputs if (g.ActiveId == id) { @@ -4768,6 +4928,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ state->Edited = false; state->BufCapacity = buf_size; state->Flags = flags; + state->WrapWidth = wrap_width; // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. // Down the line we should have a cleaner library-wide concept of Selected vs Active. @@ -4789,7 +4950,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if ((multiclick_count % 2) == 0) { // Double-click: Select word - // We always use the "Mac" word advance for double-click select vs CTRL+Right which use the platform dependent variant: + // We always use the "Mac" word advance for double-click select vs Ctrl+Right which use the platform dependent variant: // FIXME: There are likely many ways to improve this behavior, but there's no "right" behavior (depends on use-case, software, OS) const bool is_bol = (state->Stb->cursor == 0) || ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb->cursor - 1) == '\n'; if (STB_TEXT_HAS_SELECTION(state->Stb) || !is_bol) @@ -4805,9 +4966,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { // Triple-click: Select line const bool is_eol = ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb->cursor) == '\n'; + state->WrapWidth = 0.0f; // Temporarily disable wrapping so we use real line start. state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART); state->OnKeyPressed(STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT); state->OnKeyPressed(STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT); + state->WrapWidth = wrap_width; if (!is_eol && is_multiline) { ImSwap(state->Stb->select_start, state->Stb->select_end); @@ -4856,7 +5019,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } // Process regular text input (before we check for Return because using some IME will effectively send a Return?) - // We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters. + // We ignore Ctrl inputs, but need to allow Alt+Ctrl as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters. const bool ignore_char_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeyCtrl); if (io.InputQueueCharacters.Size > 0) { @@ -4889,7 +5052,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl const bool is_startend_key_down = is_osx && io.KeyCtrl && !io.KeySuper && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End - // Using Shortcut() with ImGuiInputFlags_RouteFocused (default policy) to allow routing operations for other code (e.g. calling window trying to use CTRL+A and CTRL+B: former would be handled by InputText) + // Using Shortcut() with ImGuiInputFlags_RouteFocused (default policy) to allow routing operations for other code (e.g. calling window trying to use Ctrl+A and Ctrl+B: former would be handled by InputText) // Otherwise we could simply assume that we own the keys as we are active. const ImGuiInputFlags f_repeat = ImGuiInputFlags_Repeat; const bool is_cut = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_X, f_repeat, id) || Shortcut(ImGuiMod_Shift | ImGuiKey_Delete, f_repeat, id)) && !is_readonly && !is_password && (!is_multiline || state->HasSelection()); @@ -4901,7 +5064,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful. const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; - const bool is_enter_pressed = IsKeyPressed(ImGuiKey_Enter, true) || IsKeyPressed(ImGuiKey_KeypadEnter, true); + const bool is_enter = Shortcut(ImGuiKey_Enter, f_repeat, id) || Shortcut(ImGuiKey_KeypadEnter, f_repeat, id); + const bool is_ctrl_enter = Shortcut(ImGuiMod_Ctrl | ImGuiKey_Enter, f_repeat, id) || Shortcut(ImGuiMod_Ctrl | ImGuiKey_KeypadEnter, f_repeat, id); const bool is_gamepad_validate = nav_gamepad_active && (IsKeyPressed(ImGuiKey_NavGamepadActivate, false) || IsKeyPressed(ImGuiKey_NavGamepadInput, false)); const bool is_cancel = Shortcut(ImGuiKey_Escape, f_repeat, id) || (nav_gamepad_active && Shortcut(ImGuiKey_NavGamepadCancel, f_repeat, id)); @@ -4936,11 +5100,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } state->OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); } - else if (is_enter_pressed || is_gamepad_validate) + else if (is_enter || is_ctrl_enter || is_gamepad_validate) { // Determine if we turn Enter into a \n character bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0; - if (!is_multiline || is_gamepad_validate || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl)) + if (!is_multiline || is_gamepad_validate || (ctrl_enter_for_new_line != is_ctrl_enter)) { validated = true; if (io.ConfigInputTextEnterKeepActive && !is_multiline) @@ -4950,7 +5114,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } else if (!is_readonly) { - unsigned int c = '\n'; // Insert new line + // Insert new line + unsigned int c = '\n'; if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data)) state->OnCharPressed(c); } @@ -4959,7 +5124,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { if (flags & ImGuiInputTextFlags_EscapeClearsAll) { - if (buf[0] != 0) + if (state->TextA.Data[0] != 0) { revert_edit = true; } @@ -5012,12 +5177,13 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { // Filter pasted buffer const int clipboard_len = (int)ImStrlen(clipboard); + const char* clipboard_end = clipboard + clipboard_len; ImVector clipboard_filtered; clipboard_filtered.reserve(clipboard_len + 1); for (const char* s = clipboard; *s != 0; ) { unsigned int c; - int in_len = ImTextCharFromUtf8(&c, s, NULL); + int in_len = ImTextCharFromUtf8(&c, s, clipboard_end); s += in_len; if (!InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, true)) continue; @@ -5051,20 +5217,20 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (flags & ImGuiInputTextFlags_EscapeClearsAll) { // Clear input - IM_ASSERT(buf[0] != 0); + IM_ASSERT(state->TextA.Data[0] != 0); apply_new_text = ""; apply_new_text_length = 0; value_changed = true; - IMSTB_TEXTEDIT_CHARTYPE empty_string; + char empty_string = 0; stb_textedit_replace(state, state->Stb, &empty_string, 0); } - else if (strcmp(buf, state->TextToRevertTo.Data) != 0) + else if (strcmp(state->TextA.Data, state->TextToRevertTo.Data) != 0) { apply_new_text = state->TextToRevertTo.Data; apply_new_text_length = state->TextToRevertTo.Size - 1; // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents. - // Push records into the undo stack so we can CTRL+Z the revert operation itself + // Push records into the undo stack so we can Ctrl+Z the revert operation itself value_changed = true; stb_textedit_replace(state, state->Stb, state->TextToRevertTo.Data, state->TextToRevertTo.Size - 1); } @@ -5133,10 +5299,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ callback_data.BufTextLen = state->TextLen; callback_data.BufSize = state->BufCapacity; callback_data.BufDirty = false; - - const int utf8_cursor_pos = callback_data.CursorPos = state->Stb->cursor; - const int utf8_selection_start = callback_data.SelectionStart = state->Stb->select_start; - const int utf8_selection_end = callback_data.SelectionEnd = state->Stb->select_end; + callback_data.CursorPos = state->Stb->cursor; + callback_data.SelectionStart = state->Stb->select_start; + callback_data.SelectionEnd = state->Stb->select_end; // Call user code callback(&callback_data); @@ -5146,11 +5311,12 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ IM_ASSERT(callback_data.Buf == callback_buf); // Invalid to modify those fields IM_ASSERT(callback_data.BufSize == state->BufCapacity); IM_ASSERT(callback_data.Flags == flags); - const bool buf_dirty = callback_data.BufDirty; - if (callback_data.CursorPos != utf8_cursor_pos || buf_dirty) { state->Stb->cursor = callback_data.CursorPos; state->CursorFollow = true; } - if (callback_data.SelectionStart != utf8_selection_start || buf_dirty) { state->Stb->select_start = (callback_data.SelectionStart == callback_data.CursorPos) ? state->Stb->cursor : callback_data.SelectionStart; } - if (callback_data.SelectionEnd != utf8_selection_end || buf_dirty) { state->Stb->select_end = (callback_data.SelectionEnd == callback_data.SelectionStart) ? state->Stb->select_start : callback_data.SelectionEnd; } - if (buf_dirty) + if (callback_data.BufDirty || callback_data.CursorPos != state->Stb->cursor) + state->CursorFollow = true; + state->Stb->cursor = ImClamp(callback_data.CursorPos, 0, callback_data.BufTextLen); + state->Stb->select_start = ImClamp(callback_data.SelectionStart, 0, callback_data.BufTextLen); + state->Stb->select_end = ImClamp(callback_data.SelectionEnd, 0, callback_data.BufTextLen); + if (callback_data.BufDirty) { // Callback may update buffer and thus set buf_dirty even in read-only mode. IM_ASSERT(callback_data.BufTextLen == (int)ImStrlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! @@ -5225,9 +5391,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); } - const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + inner_size.x, frame_bb.Min.y + inner_size.y); // Not using frame_bb.Max because we have adjusted size ImVec2 draw_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding; ImVec2 text_size(0.0f, 0.0f); + ImRect clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + inner_size.x, frame_bb.Min.y + inner_size.y); // Not using frame_bb.Max because we have adjusted size + if (is_multiline) + clip_rect.ClipWith(draw_window->ClipRect); // Set upper limit of single-line InputTextEx() at 2 million characters strings. The current pathological worst case is a long line // without any carriage return, which would makes ImFont::RenderText() reserve too many vertices and probably crash. Avoid it altogether. @@ -5252,15 +5420,47 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ buf_display = hint; buf_display_end = hint + ImStrlen(hint); } + else + { + if (render_cursor || render_selection || g.ActiveId == id) + buf_display_end = buf_display + state->TextLen; //-V595 + else if (is_multiline && !is_wordwrap) + buf_display_end = NULL; // Inactive multi-line: end of buffer will be output by InputTextLineIndexBuild() special strchr() path. + else + buf_display_end = buf_display + ImStrlen(buf_display); + } + + // Calculate visibility + int line_visible_n0 = 0, line_visible_n1 = 1; + if (is_multiline) + CalcClipRectVisibleItemsY(clip_rect, draw_pos, g.FontSize, &line_visible_n0, &line_visible_n1); + + // Build line index for easy data access (makes code below simpler and faster) + ImGuiTextIndex* line_index = &g.InputTextLineIndex; + line_index->Offsets.resize(0); + int line_count = 1; + if (is_multiline) + { + // If scrolling is expected to change build full index. + // FIXME-OPT: Could append to index when new value of line_visible_n1 becomes bigger, see second call to CalcClipRectVisibleItemsY() below. + bool will_scroll_y = state && ((state->CursorFollow && render_cursor) || (state->CursorCenterY && (render_cursor || render_selection))); + line_count = InputTextLineIndexBuild(flags, line_index, buf_display, buf_display_end, wrap_width, will_scroll_y ? INT_MAX : line_visible_n1 + 1, buf_display_end ? NULL : &buf_display_end); + } + line_index->EndOffset = (int)(buf_display_end - buf_display); + line_visible_n1 = ImMin(line_visible_n1, line_count); + + // Store text height (we don't need width) + text_size = ImVec2(inner_size.x, line_count * g.FontSize); + //GetForegroundDrawList()->AddRect(draw_pos + ImVec2(0, line_visible_n0 * g.FontSize), draw_pos + ImVec2(frame_size.x, line_visible_n1 * g.FontSize), IM_COL32(255, 0, 0, 255)); + + // Calculate blinking cursor position + const ImVec2 cursor_offset = render_cursor && state ? InputTextLineIndexGetPosOffset(g, state, line_index, buf_display, buf_display_end, state->Stb->cursor) : ImVec2(0.0f, 0.0f); + ImVec2 draw_scroll; // Render text. We currently only render selection when the widget is active or while scrolling. - // FIXME: We could remove the '&& render_cursor' to keep rendering selection when inactive. + const ImU32 text_col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text); if (render_cursor || render_selection) { - IM_ASSERT(state != NULL); - if (!is_displaying_hint) - buf_display_end = buf_display + state->TextLen; - // Render text (with cursor and selection) // This is going to be messy. We need to: // - Display the text (this alone can be more easily clipped) @@ -5268,48 +5468,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // - Measure text height (for scrollbar) // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort) // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8. - const char* text_begin = buf_display; - const char* text_end = text_begin + state->TextLen; - ImVec2 cursor_offset, select_start_offset; - - { - // Find lines numbers straddling cursor and selection min position - int cursor_line_no = render_cursor ? -1 : -1000; - int selmin_line_no = render_selection ? -1 : -1000; - const char* cursor_ptr = render_cursor ? text_begin + state->Stb->cursor : NULL; - const char* selmin_ptr = render_selection ? text_begin + ImMin(state->Stb->select_start, state->Stb->select_end) : NULL; - - // Count lines and find line number for cursor and selection ends - int line_count = 1; - if (is_multiline) - { - for (const char* s = text_begin; (s = (const char*)ImMemchr(s, '\n', (size_t)(text_end - s))) != NULL; s++) - { - if (cursor_line_no == -1 && s >= cursor_ptr) { cursor_line_no = line_count; } - if (selmin_line_no == -1 && s >= selmin_ptr) { selmin_line_no = line_count; } - line_count++; - } - } - if (cursor_line_no == -1) - cursor_line_no = line_count; - if (selmin_line_no == -1) - selmin_line_no = line_count; - - // Calculate 2d position by finding the beginning of the line and measuring distance - cursor_offset.x = InputTextCalcTextSize(&g, ImStrbol(cursor_ptr, text_begin), cursor_ptr).x; - cursor_offset.y = cursor_line_no * g.FontSize; - if (selmin_line_no >= 0) - { - select_start_offset.x = InputTextCalcTextSize(&g, ImStrbol(selmin_ptr, text_begin), selmin_ptr).x; - select_start_offset.y = selmin_line_no * g.FontSize; - } - - // Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224) - if (is_multiline) - text_size = ImVec2(inner_size.x, line_count * g.FontSize); - } + IM_ASSERT(state != NULL); + state->LineCount = line_count; // Scroll + float new_scroll_y = scroll_y; if (render_cursor && state->CursorFollow) { // Horizontal scroll in chunks of quarter width @@ -5324,7 +5487,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } else { - state->Scroll.y = 0.0f; + state->Scroll.x = 0.0f; } // Vertical scroll @@ -5332,103 +5495,105 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { // Test if cursor is vertically visible if (cursor_offset.y - g.FontSize < scroll_y) - scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); + new_scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); else if (cursor_offset.y - (inner_size.y - style.FramePadding.y * 2.0f) >= scroll_y) - scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f; - const float scroll_max_y = ImMax((text_size.y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f); - scroll_y = ImClamp(scroll_y, 0.0f, scroll_max_y); - draw_pos.y += (draw_window->Scroll.y - scroll_y); // Manipulate cursor pos immediately avoid a frame of lag - draw_window->Scroll.y = scroll_y; + new_scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f; } - state->CursorFollow = false; } - - // Draw selection - const ImVec2 draw_scroll = ImVec2(state->Scroll.x, 0.0f); - if (render_selection) + if (state->CursorCenterY) { - const char* text_selected_begin = text_begin + ImMin(state->Stb->select_start, state->Stb->select_end); - const char* text_selected_end = text_begin + ImMax(state->Stb->select_start, state->Stb->select_end); - - ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg, render_cursor ? 1.0f : 0.6f); // FIXME: current code flow mandate that render_cursor is always true here, we are leaving the transparent one for tests. - float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection. - float bg_offy_dn = is_multiline ? 0.0f : 2.0f; - ImVec2 rect_pos = draw_pos + select_start_offset - draw_scroll; - for (const char* p = text_selected_begin; p < text_selected_end; ) - { - if (rect_pos.y > clip_rect.w + g.FontSize) - break; - if (rect_pos.y < clip_rect.y) - { - p = (const char*)ImMemchr((void*)p, '\n', text_selected_end - p); - p = p ? p + 1 : text_selected_end; - } - else - { - ImVec2 rect_size = InputTextCalcTextSize(&g, p, text_selected_end, &p, NULL, true); - if (rect_size.x <= 0.0f) rect_size.x = IM_TRUNC(g.FontBaked->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines - ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos + ImVec2(rect_size.x, bg_offy_dn)); - rect.ClipWith(clip_rect); - if (rect.Overlaps(clip_rect)) - draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color); - rect_pos.x = draw_pos.x - draw_scroll.x; - } - rect_pos.y += g.FontSize; - } + if (is_multiline) + new_scroll_y = cursor_offset.y - g.FontSize - (inner_size.y * 0.5f - style.FramePadding.y); + state->CursorCenterY = false; + render_cursor = false; } - - // We test for 'buf_display_max_length' as a way to avoid some pathological cases (e.g. single-line 1 MB string) which would make ImDrawList crash. - // FIXME-OPT: Multiline could submit a smaller amount of contents to AddText() since we already iterated through it. - if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length) + if (new_scroll_y != scroll_y) { - ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text); - draw_window->DrawList->AddText(g.Font, g.FontSize, g.FontWeight, draw_pos - draw_scroll, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect); + const float scroll_max_y = ImMax((text_size.y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f); + scroll_y = ImClamp(new_scroll_y, 0.0f, scroll_max_y); + draw_pos.y += (draw_window->Scroll.y - scroll_y); // Manipulate cursor pos immediately avoid a frame of lag + draw_window->Scroll.y = scroll_y; + CalcClipRectVisibleItemsY(clip_rect, draw_pos, g.FontSize, &line_visible_n0, &line_visible_n1); + line_visible_n1 = ImMin(line_visible_n1, line_count); } - // Draw blinking cursor - if (render_cursor) + // Draw selection + draw_scroll.x = state->Scroll.x; + if (render_selection) { - state->CursorAnim += io.DeltaTime; - bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f; - 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->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_InputTextCursor), 1.0f); // FIXME-DPI: Cursor thickness (#7031) + const ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg, render_cursor ? 1.0f : 0.6f); // FIXME: current code flow mandate that render_cursor is always true here, we are leaving the transparent one for tests. + const float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection. + const float bg_offy_dn = is_multiline ? 0.0f : 2.0f; + const float bg_eol_width = IM_TRUNC(g.FontBaked->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines - // 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. - // As per #6341, make sure we don't set that on the deactivating frame. - if (!is_readonly && g.ActiveId == id) + const char* text_selected_begin = buf_display + ImMin(state->Stb->select_start, state->Stb->select_end); + const char* text_selected_end = buf_display + ImMax(state->Stb->select_start, state->Stb->select_end); + for (int line_n = line_visible_n0; line_n < line_visible_n1; line_n++) { - ImGuiPlatformImeData* ime_data = &g.PlatformImeData; // (this is a public struct, passed to io.Platform_SetImeDataFn() handler) - ime_data->WantVisible = true; - ime_data->WantTextInput = true; - ime_data->InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize); - ime_data->InputLineHeight = g.FontSize; - ime_data->ViewportId = window->Viewport->ID; + const char* p = line_index->get_line_begin(buf_display, line_n); + const char* p_eol = line_index->get_line_end(buf_display, line_n); + const bool p_eol_is_wrap = (p_eol < buf_display_end && *p_eol != '\n'); + if (p_eol_is_wrap) + p_eol++; + const char* line_selected_begin = (text_selected_begin > p) ? text_selected_begin : p; + const char* line_selected_end = (text_selected_end < p_eol) ? text_selected_end : p_eol; + + float rect_width = 0.0f; + if (line_selected_begin < line_selected_end) + rect_width += CalcTextSize(line_selected_begin, line_selected_end).x; + if (text_selected_begin <= p_eol && text_selected_end > p_eol && !p_eol_is_wrap) + rect_width += bg_eol_width; // So we can see selected empty lines + if (rect_width == 0.0f) + continue; + + ImRect rect; + rect.Min.x = draw_pos.x - draw_scroll.x + CalcTextSize(p, line_selected_begin).x; + rect.Min.y = draw_pos.y - draw_scroll.y + line_n * g.FontSize; + rect.Max.x = rect.Min.x + rect_width; + rect.Max.y = rect.Min.y + bg_offy_dn + g.FontSize; + rect.Min.y -= bg_offy_up; + rect.ClipWith(clip_rect); + draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color); } } } - else + + // Find render position for right alignment (single-line only) + if (g.ActiveId != id && flags & ImGuiInputTextFlags_ElideLeft) + draw_pos.x = ImMin(draw_pos.x, frame_bb.Max.x - CalcTextSize(buf_display, NULL).x - style.FramePadding.x); + //draw_scroll.x = state->Scroll.x; // Preserve scroll when inactive? + + // Render text + if ((is_multiline || (buf_display_end - buf_display) < buf_display_max_length) && (text_col & IM_COL32_A_MASK) && (line_visible_n0 < line_visible_n1)) + g.Font->RenderText(draw_window->DrawList, g.FontSize, g.FontWeight, + draw_pos - draw_scroll + ImVec2(0.0f, line_visible_n0 * g.FontSize), + text_col, clip_rect.AsVec4(), + line_index->get_line_begin(buf_display, line_visible_n0), + line_index->get_line_end(buf_display, line_visible_n1 - 1), + wrap_width, ImDrawTextFlags_WrapKeepBlanks | ImDrawTextFlags_CpuFineClip); + + // Render blinking cursor + if (render_cursor) { - // Render text only (no selection, no cursor) - if (is_multiline) - text_size = ImVec2(inner_size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_display_end) * g.FontSize); // We don't need width - else if (!is_displaying_hint && g.ActiveId == id) - buf_display_end = buf_display + state->TextLen; - else if (!is_displaying_hint) - buf_display_end = buf_display + ImStrlen(buf_display); + state->CursorAnim += io.DeltaTime; + bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f; + 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->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_InputTextCursor), 1.0f); // FIXME-DPI: Cursor thickness (#7031) - if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length) + // 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. + // As per #6341, make sure we don't set that on the deactivating frame. + if (!is_readonly && g.ActiveId == id) { - // Find render position for right alignment - if (flags & ImGuiInputTextFlags_ElideLeft) - draw_pos.x = ImMin(draw_pos.x, frame_bb.Max.x - CalcTextSize(buf_display, NULL).x - style.FramePadding.x); - - const ImVec2 draw_scroll = /*state ? ImVec2(state->Scroll.x, 0.0f) :*/ ImVec2(0.0f, 0.0f); // Preserve scroll when inactive? - ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text); - draw_window->DrawList->AddText(g.Font, g.FontSize, g.FontWeight, draw_pos - draw_scroll, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect); + ImGuiPlatformImeData* ime_data = &g.PlatformImeData; // (this is a public struct, passed to io.Platform_SetImeDataFn() handler) + ime_data->WantVisible = true; + ime_data->WantTextInput = true; + ime_data->InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize); + ime_data->InputLineHeight = g.FontSize; + ime_data->ViewportId = window->Viewport->ID; } } @@ -5484,8 +5649,10 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state) ImStb::StbUndoState* undo_state = &stb_state->undostate; Text("ID: 0x%08X, ActiveID: 0x%08X", state->ID, g.ActiveId); DebugLocateItemOnHover(state->ID); - Text("CurLenA: %d, Cursor: %d, Selection: %d..%d", state->TextLen, stb_state->cursor, stb_state->select_start, stb_state->select_end); - Text("BufCapacity: %d", state->BufCapacity); + Text("TextLen: %d, Cursor: %d%s, Selection: %d..%d", state->TextLen, stb_state->cursor, + (state->Flags & ImGuiInputTextFlags_WordWrap) ? (state->LastMoveDirectionLR == ImGuiDir_Left ? " (L)" : " (R)") : "", + stb_state->select_start, stb_state->select_end); + Text("BufCapacity: %d, LineCount: %d", state->BufCapacity, state->LineCount); Text("(Internal Buffer: TextA Size: %d, Capacity: %d)", state->TextA.Size, state->TextA.Capacity); Text("has_preferred_x: %d (%.2f)", stb_state->has_preferred_x, stb_state->preferred_x); Text("undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point); @@ -5563,7 +5730,7 @@ static void ColorEditRestoreHS(const float* col, float* H, float* S, float* V) // Edit colors components (each component in 0.0f..1.0f range). // See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. -// With typical options: Left-click on color square to open color picker. Right-click to open option menu. CTRL+Click over input fields to edit them and TAB to go to next item. +// With typical options: Left-click on color square to open color picker. Right-click to open option menu. Ctrl+Click over input fields to edit them and TAB to go to next item. bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags) { ImGuiWindow* window = GetCurrentWindow(); @@ -5635,8 +5802,10 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag { // RGB/HSV 0..255 Sliders const float w_items = w_inputs - style.ItemInnerSpacing.x * (components - 1); + const float w_per_component = IM_TRUNC(w_items / components); + const bool draw_color_marker = (flags & (ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_NoColorMarkers)) == 0; - const bool hide_prefix = (IM_TRUNC(w_items / components) <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x); + const bool hide_prefix = draw_color_marker || (w_per_component <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x); static const char* ids[4] = { "##X", "##Y", "##Z", "##W" }; static const char* fmt_table_int[3][4] = { @@ -5651,6 +5820,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag { "H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f" } // Long display for HSVA }; const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_DisplayHSV) ? 2 : 1; + const ImGuiSliderFlags drag_flags = draw_color_marker ? ImGuiSliderFlags_ColorMarkers : ImGuiSliderFlags_None; float prev_split = 0.0f; for (int n = 0; n < components; n++) @@ -5660,16 +5830,18 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag float next_split = IM_TRUNC(w_items * (n + 1) / components); SetNextItemWidth(ImMax(next_split - prev_split, 1.0f)); prev_split = next_split; + if (draw_color_marker) + SetNextItemColorMarker(GDefaultRgbaColorMarkers[n]); // FIXME: When ImGuiColorEditFlags_HDR flag is passed HS values snap in weird ways when SV values go below 0. if (flags & ImGuiColorEditFlags_Float) { - value_changed |= DragFloat(ids[n], &f[n], 1.0f / 255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]); + value_changed |= DragFloat(ids[n], &f[n], 1.0f / 255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n], drag_flags); value_changed_as_float |= value_changed; } else { - value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]); + value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n], drag_flags); } if (!(flags & ImGuiColorEditFlags_NoOptions)) OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); @@ -5680,11 +5852,11 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag // RGB Hexadecimal Input char buf[64]; if (alpha) - ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255), ImClamp(i[3], 0, 255)); + ImFormatString(buf, IM_COUNTOF(buf), "#%02X%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255), ImClamp(i[3], 0, 255)); else - ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255)); + ImFormatString(buf, IM_COUNTOF(buf), "#%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255)); SetNextItemWidth(w_inputs); - if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsUppercase)) + if (InputText("##Text", buf, IM_COUNTOF(buf), ImGuiInputTextFlags_CharsUppercase)) { value_changed = true; char* p = buf; @@ -6378,18 +6550,18 @@ void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) { int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); char buf[64]; - ImFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff, %.3ff)", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); + ImFormatString(buf, IM_COUNTOF(buf), "(%.3ff, %.3ff, %.3ff, %.3ff)", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); if (Selectable(buf)) SetClipboardText(buf); - ImFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d,%d)", cr, cg, cb, ca); + ImFormatString(buf, IM_COUNTOF(buf), "(%d,%d,%d,%d)", cr, cg, cb, ca); if (Selectable(buf)) SetClipboardText(buf); - ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", cr, cg, cb); + ImFormatString(buf, IM_COUNTOF(buf), "#%02X%02X%02X", cr, cg, cb); if (Selectable(buf)) SetClipboardText(buf); if (!(flags & ImGuiColorEditFlags_NoAlpha)) { - ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", cr, cg, cb, ca); + ImFormatString(buf, IM_COUNTOF(buf), "#%02X%02X%02X%02X", cr, cg, cb, ca); if (Selectable(buf)) SetClipboardText(buf); } @@ -6630,7 +6802,6 @@ static void TreeNodeStoreStackData(ImGuiTreeNodeFlags flags, float x1) window->DC.TreeRecordsClippedNodesY2Mask |= (1 << window->DC.TreeDepth); } -// When using public API, currently 'id == storage_id' is always true, but we separate the values to facilitate advanced user code doing storage queries outside of UI loop. bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end) { ImGuiWindow* window = GetCurrentWindow(); @@ -6639,26 +6810,28 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; + + // When not framed, we vertically increase height up to typical framed widget height const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0; - const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, ImMin(window->DC.CurrLineTextBaseOffset, style.FramePadding.y)); + const bool use_frame_padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)); + const ImVec2 padding = use_frame_padding ? style.FramePadding : ImVec2(style.FramePadding.x, ImMin(window->DC.CurrLineTextBaseOffset, style.FramePadding.y)); if (!label_end) label_end = FindRenderedTextEnd(label); const ImVec2 label_size = CalcTextSize(label, label_end, false); const float text_offset_x = g.FontSize + (display_frame ? padding.x * 3 : padding.x * 2); // Collapsing arrow width + Spacing - const float text_offset_y = ImMax(padding.y, window->DC.CurrLineTextBaseOffset); // Latch before ItemSize changes it + const float text_offset_y = use_frame_padding ? ImMax(style.FramePadding.y, window->DC.CurrLineTextBaseOffset) : window->DC.CurrLineTextBaseOffset; // Latch before ItemSize changes it const float text_width = g.FontSize + label_size.x + padding.x * 2; // Include collapsing arrow - // We vertically grow up to current line height up the typical widget height. - const float frame_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y * 2), label_size.y + padding.y * 2); + const float frame_height = label_size.y + padding.y * 2; const bool span_all_columns = (flags & ImGuiTreeNodeFlags_SpanAllColumns) != 0 && (g.CurrentTable != NULL); const bool span_all_columns_label = (flags & ImGuiTreeNodeFlags_LabelSpanAllColumns) != 0 && (g.CurrentTable != NULL); ImRect frame_bb; frame_bb.Min.x = span_all_columns ? window->ParentWorkRect.Min.x : (flags & ImGuiTreeNodeFlags_SpanFullWidth) ? window->WorkRect.Min.x : window->DC.CursorPos.x; - frame_bb.Min.y = window->DC.CursorPos.y; + frame_bb.Min.y = window->DC.CursorPos.y + (text_offset_y - padding.y); frame_bb.Max.x = span_all_columns ? window->ParentWorkRect.Max.x : (flags & ImGuiTreeNodeFlags_SpanLabelWidth) ? window->DC.CursorPos.x + text_width + padding.x : window->WorkRect.Max.x; - frame_bb.Max.y = window->DC.CursorPos.y + frame_height; + frame_bb.Max.y = window->DC.CursorPos.y + (text_offset_y - padding.y) + frame_height; if (display_frame) { const float outer_extend = IM_TRUNC(window->WindowPadding.x * 0.5f); // Framed header expand a little outside of current limits @@ -7213,6 +7386,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); + bool auto_selected = false; // Multi-selection support (footer) if (is_multi_select) @@ -7229,8 +7403,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl // - (2) usage will fail with clipped items // The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API. if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == g.CurrentFocusScopeId) - if (g.NavJustMovedToId == id) - selected = pressed = true; + if (g.NavJustMovedToId == id && (g.NavJustMovedToKeyMods & ImGuiMod_Ctrl) == 0) + selected = pressed = auto_selected = true; } // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with keyboard/gamepad @@ -7281,7 +7455,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl RenderTextClipped(pos, ImVec2(ImMin(pos.x + size.x, window->WorkRect.Max.x), pos.y + size.y), label, NULL, &label_size, style.SelectableTextAlign, &bb); // Automatically close popups - if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (g.LastItemData.ItemFlags & ImGuiItemFlags_AutoClosePopups)) + if (pressed && !auto_selected && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (g.LastItemData.ItemFlags & ImGuiItemFlags_AutoClosePopups)) CloseCurrentPopup(); if (disabled_item && !disabled_global) @@ -7340,7 +7514,7 @@ ImGuiTypingSelectRequest* ImGui::GetTypingSelectRequest(ImGuiTypingSelectFlags f } // Append to buffer - const int buffer_max_len = IM_ARRAYSIZE(data->SearchBuffer) - 1; + const int buffer_max_len = IM_COUNTOF(data->SearchBuffer) - 1; int buffer_len = (int)ImStrlen(data->SearchBuffer); bool select_request = false; for (ImWchar w : g.IO.InputQueueCharacters) @@ -7802,7 +7976,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int sel } } - // Shortcut: Select all (CTRL+A) + // Shortcut: Select all (Ctrl+A) if (!(flags & ImGuiMultiSelectFlags_SingleSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll)) if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_A)) request_select_all = true; @@ -7947,7 +8121,7 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags if (ms->LoopRequestSetAll != -1) selected = (ms->LoopRequestSetAll == 1); - // When using SHIFT+Nav: because it can incur scrolling we cannot afford a frame of lag with the selection highlight (otherwise scrolling would happen before selection) + // When using Shift+Nav: because it can incur scrolling we cannot afford a frame of lag with the selection highlight (otherwise scrolling would happen before selection) // For this to work, we need someone to set 'RangeSrcPassedBy = true' at some point (either clipper either SetNextItemSelectionUserData() function) if (ms->IsKeyboardSetRange) { @@ -7978,7 +8152,7 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags // Alter button behavior flags // To handle drag and drop of multiple items we need to avoid clearing selection on click. - // Enabling this test makes actions using CTRL+SHIFT delay their effect on MouseUp which is annoying, but it allows drag and drop of multiple items. + // Enabling this test makes actions using Ctrl+Shift delay their effect on MouseUp which is annoying, but it allows drag and drop of multiple items. if (p_button_flags != NULL) { ImGuiButtonFlags button_flags = *p_button_flags; @@ -8082,8 +8256,8 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) } // Right-click handling. - // FIXME-MULTISELECT: Currently filtered out by ImGuiMultiSelectFlags_NoAutoSelect but maybe should be moved to Selectable(). See https://github.com/ocornut/imgui/pull/5816 - if (hovered && IsMouseClicked(1) && (flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0) + // FIXME-MULTISELECT: Maybe should be moved to Selectable()? Also see #5816, #8200, #9015 + if (hovered && IsMouseClicked(1) && (flags & (ImGuiMultiSelectFlags_NoAutoSelect | ImGuiMultiSelectFlags_NoSelectOnRightClick)) == 0) { if (g.ActiveId != 0 && g.ActiveId != id) ClearActiveID(); @@ -8177,7 +8351,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) MultiSelectAddSetRange(ms, range_selected, range_direction, storage->RangeSrcItem, item_data); } - // Update/store the selection state of the Source item (used by CTRL+SHIFT, when Source is unselected we perform a range unselect) + // Update/store the selection state of the Source item (used by Ctrl+Shift, when Source is unselected we perform a range unselect) if (storage->RangeSrcItem == item_data) storage->RangeSelected = selected ? 1 : 0; @@ -8359,7 +8533,7 @@ void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) // - Optimized select can append unsorted, then sort in a second pass. Optimized unselect can clear in-place then compact in a second pass. // - A more optimal version wouldn't even use ImGuiStorage but directly a ImVector to reduce bandwidth, but this is a reasonable trade off to reuse code. // - There are many ways this could be better optimized. The worse case scenario being: using BoxSelect2d in a grid, box-select scrolling down while wiggling - // left and right: it affects coarse clipping + can emit multiple SetRange with 1 item each.) + // left and right: it affects coarse clipping + can emit multiple SetRange with 1 item each. // FIXME-OPT: For each block of consecutive SetRange request: // - add all requests to a sorted list, store ID, selected, offset in ImGuiStorage. // - rewrite sorted storage a single time. @@ -8391,7 +8565,7 @@ void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) else { // Append insertion + single sort likely be faster. - // Use req.RangeDirection to set order field so that shift+clicking from 1 to 5 is different than shift+clicking from 5 to 1 + // Use req.RangeDirection to set order field so that Shift+Clicking from 1 to 5 is different than Shift+Clicking from 5 to 1 const int size_before_amends = _Storage.Data.Size; int selection_order = _SelectionOrder + ((req.RangeDirection < 0) ? selection_changes - 1 : 0); for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++, selection_order += req.RangeDirection) @@ -8740,7 +8914,7 @@ void ImGui::Value(const char* prefix, float v, const char* float_format) if (float_format) { char fmt[64]; - ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format); + ImFormatString(fmt, IM_COUNTOF(fmt), "%%s: %s", float_format); Text(fmt, prefix, v); } else @@ -8779,7 +8953,7 @@ void ImGuiMenuColumns::CalcNextTotalWidth(bool update_offsets) { ImU16 offset = 0; bool want_spacing = false; - for (int i = 0; i < IM_ARRAYSIZE(Widths); i++) + for (int i = 0; i < IM_COUNTOF(Widths); i++) { ImU16 width = Widths[i]; if (want_spacing && width > 0) @@ -8879,6 +9053,7 @@ void ImGui::EndMenuBar() PopClipRect(); PopID(); + IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'" window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->Pos.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos. // FIXME: Extremely confusing, cleanup by (a) working on WorkRect stack system (b) not using a Group confusingly here. @@ -9044,7 +9219,8 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) // 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(). // e.g. Menus tend to overlap each other horizontally to amplify relative Z-ordering. - ImVec2 popup_pos, pos = window->DC.CursorPos; + ImVec2 popup_pos; + ImVec2 pos = window->DC.CursorPos; PushID(label); if (!enabled) BeginDisabled(); @@ -9058,34 +9234,34 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) // Menu inside a horizontal menu bar // Selectable extend their highlight by half ItemSpacing in each direction. // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin() - popup_pos = ImVec2(pos.x - 1.0f - IM_TRUNC(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight); window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f); PushStyleVarX(ImGuiStyleVar_ItemSpacing, style.ItemSpacing.x * 2.0f); float w = label_size.x; - ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); + ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, pos.y + window->DC.CurrLineTextBaseOffset); pressed = Selectable("", menu_is_open, selectable_flags, ImVec2(w, label_size.y)); LogSetNextTextDecoration("[", "]"); RenderText(text_pos, label); PopStyleVar(); window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). + popup_pos = ImVec2(pos.x - 1.0f - IM_TRUNC(style.ItemSpacing.x * 0.5f), text_pos.y - style.FramePadding.y + window->MenuBarHeight); } else { // Menu inside a regular/vertical menu // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. - // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. - popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y); + // 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 checkmark_w = IM_TRUNC(g.FontSize * 1.20f); float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, 0.0f, checkmark_w); // Feedback to next frame float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); - ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); + ImVec2 text_pos(window->DC.CursorPos.x, pos.y + window->DC.CurrLineTextBaseOffset); pressed = Selectable("", menu_is_open, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y)); LogSetNextTextDecoration("", ">"); - RenderText(text_pos, label); + RenderText(ImVec2(text_pos.x + offsets->OffsetLabel, text_pos.y), label); if (icon_w > 0.0f) - RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); - RenderArrow(window->DrawList, pos + ImVec2(offsets->OffsetMark + extra_w + g.FontSize * 0.30f, 0.0f), GetColorU32(ImGuiCol_Text), ImGuiDir_Right); + RenderText(ImVec2(text_pos.x + offsets->OffsetIcon, text_pos.y), icon); + 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); } if (!enabled) EndDisabled(); @@ -9280,27 +9456,28 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut { // Menu item inside a vertical menu // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. - // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. + // 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 min_w = window->DC.MenuColumns.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); pressed = Selectable("", false, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y)); if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) { - RenderText(pos + ImVec2(offsets->OffsetLabel, 0.0f), label); + RenderText(text_pos + ImVec2(offsets->OffsetLabel, 0.0f), label); if (icon_w > 0.0f) - RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); + RenderText(text_pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); if (shortcut_w > 0.0f) { PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); LogSetNextTextDecoration("(", ")"); - RenderText(pos + ImVec2(offsets->OffsetShortcut + stretch_w, 0.0f), shortcut, NULL, false); + RenderText(text_pos + ImVec2(offsets->OffsetShortcut + stretch_w, 0.0f), shortcut, NULL, false); PopStyleColor(); } if (selected) - RenderCheckMark(window->DrawList, pos + ImVec2(offsets->OffsetMark + stretch_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(ImGuiCol_Text), g.FontSize * 0.866f); + 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); } } IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0)); @@ -9358,6 +9535,7 @@ struct ImGuiTabBarSection { int TabCount; // Number of tabs in this section. float Width; // Sum of width of tabs in this section (after shrinking down) + float WidthAfterShrinkMinWidth; float Spacing; // Horizontal spacing at the end of the section. ImGuiTabBarSection() { memset(this, 0, sizeof(*this)); } @@ -9418,6 +9596,19 @@ static ImGuiPtrOrIndex GetTabBarRefFromTabBar(ImGuiTabBar* tab_bar) return ImGuiPtrOrIndex(tab_bar); } +ImGuiTabBar* ImGui::TabBarFindByID(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + return g.TabBars.GetByKey(id); +} + +// Remove TabBar data (currently only used by TestEngine) +void ImGui::TabBarRemove(ImGuiTabBar* tab_bar) +{ + ImGuiContext& g = *GImGui; + g.TabBars.Remove(tab_bar->ID, tab_bar); +} + bool ImGui::BeginTabBar(const char* str_id, ImGuiTabBarFlags flags) { ImGuiContext& g = *GImGui; @@ -9429,8 +9620,8 @@ bool ImGui::BeginTabBar(const char* str_id, ImGuiTabBarFlags flags) ImGuiTabBar* tab_bar = g.TabBars.GetOrAddByKey(id); ImRect tab_bar_bb = ImRect(window->DC.CursorPos.x, window->DC.CursorPos.y, window->WorkRect.Max.x, window->DC.CursorPos.y + g.FontSize + g.Style.FramePadding.y * 2); tab_bar->ID = id; - tab_bar->SeparatorMinX = tab_bar->BarRect.Min.x - IM_TRUNC(window->WindowPadding.x * 0.5f); - tab_bar->SeparatorMaxX = tab_bar->BarRect.Max.x + IM_TRUNC(window->WindowPadding.x * 0.5f); + tab_bar->SeparatorMinX = tab_bar_bb.Min.x - IM_TRUNC(window->WindowPadding.x * 0.5f); + tab_bar->SeparatorMaxX = tab_bar_bb.Max.x + IM_TRUNC(window->WindowPadding.x * 0.5f); //if (g.NavWindow && IsWindowChildOf(g.NavWindow, window, false, false)) flags |= ImGuiTabBarFlags_IsFocused; return BeginTabBarEx(tab_bar, tab_bar_bb, flags); @@ -9550,6 +9741,10 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) ImGuiContext& g = *GImGui; tab_bar->WantLayout = false; + // Track selected tab when resizing our parent down + const bool scroll_to_selected_tab = (tab_bar->BarRectPrevWidth > tab_bar->BarRect.GetWidth()); + tab_bar->BarRectPrevWidth = tab_bar->BarRect.GetWidth(); + // Garbage collect by compacting list // Detect if we need to sort out tab list (e.g. in rare case where a tab changed section) int tab_dst_n = 0; @@ -9594,8 +9789,9 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerBySection); // Calculate spacing between sections - sections[0].Spacing = sections[0].TabCount > 0 && (sections[1].TabCount + sections[2].TabCount) > 0 ? g.Style.ItemInnerSpacing.x : 0.0f; - sections[1].Spacing = sections[1].TabCount > 0 && sections[2].TabCount > 0 ? g.Style.ItemInnerSpacing.x : 0.0f; + const float tab_spacing = g.Style.ItemInnerSpacing.x; + sections[0].Spacing = sections[0].TabCount > 0 && (sections[1].TabCount + sections[2].TabCount) > 0 ? tab_spacing : 0.0f; + sections[1].Spacing = sections[1].TabCount > 0 && sections[2].TabCount > 0 ? tab_spacing : 0.0f; // Setup next selected tab ImGuiID scroll_to_tab_id = 0; @@ -9626,6 +9822,9 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) int shrink_buffer_indexes[3] = { 0, sections[0].TabCount + sections[2].TabCount, sections[0].TabCount }; g.ShrinkWidthBuffer.resize(tab_bar->Tabs.Size); + // Minimum shrink width + const float shrink_min_width = (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyMixed) ? g.Style.TabMinWidthShrink : 1.0f; + // Compute ideal tabs widths + store them into shrink buffer ImGuiTabItem* most_recently_selected_tab = NULL; int curr_section_n = -1; @@ -9648,10 +9847,13 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) const char* tab_name = TabBarGetTabName(tab_bar, tab); const bool has_close_button_or_unsaved_marker = (tab->Flags & ImGuiTabItemFlags_NoCloseButton) == 0 || (tab->Flags & ImGuiTabItemFlags_UnsavedDocument); tab->ContentWidth = (tab->RequestedWidth >= 0.0f) ? tab->RequestedWidth : TabItemCalcSize(tab_name, has_close_button_or_unsaved_marker).x; + if ((tab->Flags & ImGuiTabItemFlags_Button) == 0) + tab->ContentWidth = ImMax(tab->ContentWidth, g.Style.TabMinWidthBase); int section_n = TabItemGetSectionIdx(tab); ImGuiTabBarSection* section = §ions[section_n]; - section->Width += tab->ContentWidth + (section_n == curr_section_n ? g.Style.ItemInnerSpacing.x : 0.0f); + section->Width += tab->ContentWidth + (section_n == curr_section_n ? tab_spacing : 0.0f); + section->WidthAfterShrinkMinWidth += ImMin(tab->ContentWidth, shrink_min_width) + (section_n == curr_section_n ? tab_spacing : 0.0f); curr_section_n = section_n; // Store data so we can build an array sorted by width if we need to shrink tabs down @@ -9663,19 +9865,28 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) } // Compute total ideal width (used for e.g. auto-resizing a window) + float width_all_tabs_after_min_width_shrink = 0.0f; tab_bar->WidthAllTabsIdeal = 0.0f; for (int section_n = 0; section_n < 3; section_n++) + { tab_bar->WidthAllTabsIdeal += sections[section_n].Width + sections[section_n].Spacing; + width_all_tabs_after_min_width_shrink += sections[section_n].WidthAfterShrinkMinWidth + sections[section_n].Spacing; + } // Horizontal scrolling buttons - // (note that TabBarScrollButtons() will alter BarRect.Max.x) - if ((tab_bar->WidthAllTabsIdeal > tab_bar->BarRect.GetWidth() && tab_bar->Tabs.Size > 1) && !(tab_bar->Flags & ImGuiTabBarFlags_NoTabListScrollingButtons) && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll)) + // Important: note that TabBarScrollButtons() will alter BarRect.Max.x. + const bool can_scroll = (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll) || (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyMixed); + const float width_all_tabs_to_use_for_scroll = (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll) ? tab_bar->WidthAllTabs : width_all_tabs_after_min_width_shrink; + tab_bar->ScrollButtonEnabled = ((width_all_tabs_to_use_for_scroll > tab_bar->BarRect.GetWidth() && tab_bar->Tabs.Size > 1) && !(tab_bar->Flags & ImGuiTabBarFlags_NoTabListScrollingButtons) && can_scroll); + if (tab_bar->ScrollButtonEnabled) if (ImGuiTabItem* scroll_and_select_tab = TabBarScrollingButtons(tab_bar)) { scroll_to_tab_id = scroll_and_select_tab->ID; if ((scroll_and_select_tab->Flags & ImGuiTabItemFlags_Button) == 0) tab_bar->SelectedTabId = scroll_to_tab_id; } + if (scroll_to_tab_id == 0 && scroll_to_selected_tab) + scroll_to_tab_id = tab_bar->SelectedTabId; // Shrink widths if full tabs don't fit in their allocated space float section_0_w = sections[0].Width + sections[0].Spacing; @@ -9689,11 +9900,12 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) width_excess = (section_0_w + section_2_w) - tab_bar->BarRect.GetWidth(); // Excess used to shrink leading/trailing section // With ImGuiTabBarFlags_FittingPolicyScroll policy, we will only shrink leading/trailing if the central section is not visible anymore - if (width_excess >= 1.0f && ((tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyResizeDown) || !central_section_is_visible)) + const bool can_shrink = (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyShrink) || (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyMixed); + if (width_excess >= 1.0f && (can_shrink || !central_section_is_visible)) { int shrink_data_count = (central_section_is_visible ? sections[1].TabCount : sections[0].TabCount + sections[2].TabCount); int shrink_data_offset = (central_section_is_visible ? sections[0].TabCount + sections[2].TabCount : 0); - ShrinkWidths(g.ShrinkWidthBuffer.Data + shrink_data_offset, shrink_data_count, width_excess); + ShrinkWidths(g.ShrinkWidthBuffer.Data + shrink_data_offset, shrink_data_count, width_excess, shrink_min_width); // Apply shrunk values into tabs and sections for (int tab_n = shrink_data_offset; tab_n < shrink_data_offset + shrink_data_count; tab_n++) @@ -9736,7 +9948,8 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) tab_bar->TabsNames.Buf.resize(0); // If we have lost the selected tab, select the next most recently active one - if (found_selected_tab_id == false) + const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount); + if (found_selected_tab_id == false && !tab_bar_appearing) tab_bar->SelectedTabId = 0; if (tab_bar->SelectedTabId == 0 && tab_bar->NextSelectedTabId == 0 && most_recently_selected_tab != NULL) scroll_to_tab_id = tab_bar->SelectedTabId = most_recently_selected_tab->ID; @@ -9748,7 +9961,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) // Apply request requests if (scroll_to_tab_id != 0) TabBarScrollToTab(tab_bar, scroll_to_tab_id, sections); - else if ((tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll) && IsMouseHoveringRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, true) && IsWindowContentHoverable(g.CurrentWindow)) + else if (tab_bar->ScrollButtonEnabled && IsMouseHoveringRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, true) && IsWindowContentHoverable(g.CurrentWindow)) { const float wheel = g.IO.MouseWheelRequestAxisSwap ? g.IO.MouseWheel : g.IO.MouseWheelH; const ImGuiKey wheel_key = g.IO.MouseWheelRequestAxisSwap ? ImGuiKey_MouseWheelY : ImGuiKey_MouseWheelX; @@ -9945,6 +10158,7 @@ void ImGui::TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, ImGuiTabItem* s if ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) == 0) return; + const float tab_spacing = g.Style.ItemInnerSpacing.x; const bool is_central_section = (src_tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0; const float bar_offset = tab_bar->BarRect.Min.x - (is_central_section ? tab_bar->ScrollingTarget : 0); @@ -9963,8 +10177,8 @@ void ImGui::TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, ImGuiTabItem* s dst_idx = i; // Include spacing after tab, so when mouse cursor is between tabs we would not continue checking further tabs that are not hovered. - const float x1 = bar_offset + dst_tab->Offset - g.Style.ItemInnerSpacing.x; - const float x2 = bar_offset + dst_tab->Offset + dst_tab->Width + g.Style.ItemInnerSpacing.x; + const float x1 = bar_offset + dst_tab->Offset - tab_spacing; + const float x2 = bar_offset + dst_tab->Offset + dst_tab->Width + tab_spacing; //GetForegroundDrawList()->AddRect(ImVec2(x1, tab_bar->BarRect.Min.y), ImVec2(x2, tab_bar->BarRect.Max.y), IM_COL32(255, 0, 0, 255)); if ((dir < 0 && mouse_pos.x > x1) || (dir > 0 && mouse_pos.x < x2)) break; @@ -10022,7 +10236,7 @@ static ImGuiTabItem* ImGui::TabBarScrollingButtons(ImGuiTabBar* tab_bar) PushStyleColor(ImGuiCol_Text, arrow_col); PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); - PushItemFlag(ImGuiItemFlags_ButtonRepeat, true); + PushItemFlag(ImGuiItemFlags_ButtonRepeat | ImGuiItemFlags_NoNav, true); const float backup_repeat_delay = g.IO.KeyRepeatDelay; const float backup_repeat_rate = g.IO.KeyRepeatRate; g.IO.KeyRepeatDelay = 0.250f; @@ -10282,7 +10496,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, } // Lock visibility - // (Note: tab_contents_visible != tab_selected... because CTRL+TAB operations may preview some tabs without selecting them!) + // (Note: tab_contents_visible != tab_selected... because Ctrl+Tab operations may preview some tabs without selecting them!) bool tab_contents_visible = (tab_bar->VisibleTabId == id); if (tab_contents_visible) tab_bar->VisibleTabWasSubmitted = true; @@ -10558,8 +10772,8 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, const bool unsaved_marker_visible = (flags & ImGuiTabItemFlags_UnsavedDocument) != 0 && (button_pos.x + button_sz <= bb.Max.x) && (!close_button_visible || !is_hovered); if (unsaved_marker_visible) { - const ImRect bullet_bb(button_pos, button_pos + ImVec2(button_sz, button_sz)); - RenderBullet(draw_list, bullet_bb.GetCenter(), GetColorU32(ImGuiCol_Text)); + ImVec2 bullet_pos = button_pos + ImVec2(button_sz, button_sz) * 0.5f; + RenderBullet(draw_list, bullet_pos, GetColorU32(ImGuiCol_UnsavedMarker)); } else if (close_button_visible) { diff --git a/src/core/fullscreenui_achievements.cpp b/src/core/fullscreenui_achievements.cpp index d855447c8..1690551f5 100644 --- a/src/core/fullscreenui_achievements.cpp +++ b/src/core/fullscreenui_achievements.cpp @@ -338,8 +338,9 @@ void FullscreenUI::DrawAchievementsPauseMenuOverlays(float start_pos_y) progress_rounding); if (summary.num_unlocked_achievements > 0) { - ImGui::RenderRectFilledRangeH(dl, progress_bb, ImGui::GetColorU32(DarkerColor(UIStyle.SecondaryColor)), 0.0f, - unlocked_fraction, progress_rounding); + ImGui::RenderRectFilledInRangeH( + dl, progress_bb, ImGui::GetColorU32(DarkerColor(UIStyle.SecondaryColor)), progress_bb.Min.x, + progress_bb.Min.x + (unlocked_fraction * progress_bb.GetWidth()), progress_rounding); } buffer.format("{}/{}", summary.num_unlocked_achievements, summary.num_core_achievements); @@ -400,8 +401,9 @@ void FullscreenUI::DrawAchievementsPauseMenuOverlays(float start_pos_y) progress_rounding); if (measured_percent > 0.0f) { - ImGui::RenderRectFilledRangeH(dl, progress_bb, ImGui::GetColorU32(DarkerColor(UIStyle.SecondaryColor)), 0.0f, - measured_percent * 0.01f, progress_rounding); + ImGui::RenderRectFilledInRangeH( + dl, progress_bb, ImGui::GetColorU32(DarkerColor(UIStyle.SecondaryColor)), progress_bb.Min.x, + progress_bb.Min.x + ((measured_percent * 0.01f) * progress_bb.GetWidth()), progress_rounding); } const ImVec2 measured_progress_size = @@ -1016,8 +1018,8 @@ void FullscreenUI::DrawAchievementsWindow() progress_rounding); if (summary.num_unlocked_achievements > 0) { - ImGui::RenderRectFilledRangeH(dl, progress_bb, ImGui::GetColorU32(UIStyle.SecondaryColor), 0.0f, fraction, - progress_rounding); + ImGui::RenderRectFilledInRangeH(dl, progress_bb, ImGui::GetColorU32(UIStyle.SecondaryColor), progress_bb.Min.x, + progress_bb.Min.x + (fraction * progress_bb.GetWidth()), progress_rounding); } text.format("{}%", static_cast(std::round(fraction * 100.0f))); @@ -1288,8 +1290,8 @@ void FullscreenUI::DrawAchievement(const rc_client_achievement_t* cheevo) const float fraction = cheevo->measured_percent * 0.01f; dl->AddRectFilled(progress_bb.Min, progress_bb.Max, ImGui::GetColorU32(UIStyle.PrimaryDarkColor), progress_rounding); - ImGui::RenderRectFilledRangeH(dl, progress_bb, ImGui::GetColorU32(UIStyle.SecondaryColor), 0.0f, fraction, - progress_rounding); + ImGui::RenderRectFilledInRangeH(dl, progress_bb, ImGui::GetColorU32(UIStyle.SecondaryColor), progress_bb.Min.x, + progress_bb.Min.x + (fraction * progress_bb.GetWidth()), progress_rounding); const ImVec2 text_size = UIStyle.Font->CalcTextSizeA(subtitle_font_size, subtitle_font_weight, FLT_MAX, 0.0f, IMSTR_START_END(measured_progress)); diff --git a/src/core/fullscreenui_widgets.cpp b/src/core/fullscreenui_widgets.cpp index c97bf3b18..e121f5c0d 100644 --- a/src/core/fullscreenui_widgets.cpp +++ b/src/core/fullscreenui_widgets.cpp @@ -4821,8 +4821,8 @@ void FullscreenUI::DrawLoadingScreen(std::string_view image, std::string_view ti if (has_progress) { const float fraction = static_cast(progress_value) / static_cast(progress_max - progress_min); - ImGui::RenderRectFilledRangeH(dl, ImRect(box_start, box_end), ImGui::GetColorU32(UIStyle.SecondaryColor), 0.0f, - fraction, frame_rounding); + ImGui::RenderRectFilledInRangeH(dl, ImRect(box_start, box_end), ImGui::GetColorU32(UIStyle.SecondaryColor), + box_start.x, box_start.x + (fraction * content_width), frame_rounding); } else {