From ac7d6fb5ca0430bc1b7a15048e24d538a8d41679 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 9 Jul 2024 16:37:17 +0200 Subject: [PATCH 01/17] Internals: Added TreeNodeIsOpen() to facilitate discoverability. (#7553, #1131, #2958, #2079, #722) --- imgui_internal.h | 1 + imgui_widgets.cpp | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 5a0fc7c4b..11ada4bab 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3469,6 +3469,7 @@ namespace ImGui IMGUI_API bool SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f, ImU32 bg_col = 0); IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); IMGUI_API void TreePushOverrideID(ImGuiID id); + IMGUI_API bool TreeNodeIsOpen(ImGuiID id); IMGUI_API void TreeNodeSetOpen(ImGuiID id, bool open); IMGUI_API bool TreeNodeUpdateNextOpen(ImGuiID id, ImGuiTreeNodeFlags flags); // Return open state. Consume previous SetNextItemOpen() data, if any. May return true when logging. IMGUI_API void SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 05e470195..558979b98 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6257,6 +6257,13 @@ bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char return TreeNodeBehavior(window->GetID(ptr_id), flags, label, label_end); } +bool ImGui::TreeNodeIsOpen(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + ImGuiStorage* storage = g.CurrentWindow->DC.StateStorage; + return storage->GetInt(id, 0) != 0; +} + void ImGui::TreeNodeSetOpen(ImGuiID id, bool open) { ImGuiContext& g = *GImGui; @@ -6269,7 +6276,7 @@ bool ImGui::TreeNodeUpdateNextOpen(ImGuiID id, ImGuiTreeNodeFlags flags) if (flags & ImGuiTreeNodeFlags_Leaf) return true; - // We only write to the tree storage if the user clicks (or explicitly use the SetNextItemOpen function) + // We only write to the tree storage if the user clicks, or explicitly use the SetNextItemOpen function ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImGuiStorage* storage = window->DC.StateStorage; @@ -6311,6 +6318,7 @@ bool ImGui::TreeNodeUpdateNextOpen(ImGuiID id, ImGuiTreeNodeFlags flags) } // Store ImGuiTreeNodeStackData for just submitted node. +// Currently only supports 32 level deep and we are fine with (1 << Depth) overflowing into a zero, easy to increase. static void TreeNodeStoreStackData(ImGuiTreeNodeFlags flags) { ImGuiContext& g = *GImGui; @@ -6393,7 +6401,6 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l // Store data for the current depth to allow returning to this node from any child item. // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop(). // It will become tempting to enable ImGuiTreeNodeFlags_NavLeftJumpsBackHere by default or move it to ImGuiStyle. - // Currently only supports 32 level deep and we are fine with (1 << Depth) overflowing into a zero, easy to increase. bool store_tree_node_stack_data = false; if (!(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) { @@ -6517,7 +6524,6 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l text_pos.x -= text_offset_x -padding.x; if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton) frame_bb.Max.x -= g.FontSize + style.FramePadding.x; - if (g.LogEnabled) LogSetNextTextDecoration("###", "###"); } @@ -6550,7 +6556,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l if (store_tree_node_stack_data && is_open) TreeNodeStoreStackData(flags); // Call before TreePushOverrideID() if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) - TreePushOverrideID(id); + TreePushOverrideID(id); // Could use TreePush(label) but this avoid computing twice IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); return is_open; From bc9e5b62b6beddf6173249202a176725064ec540 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Jul 2024 16:49:35 +0200 Subject: [PATCH 02/17] Added ImGuiDataType_Bool for convenience. --- imgui.h | 3 ++- imgui_widgets.cpp | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/imgui.h b/imgui.h index b1796242b..a2c2e8c08 100644 --- a/imgui.h +++ b/imgui.h @@ -28,7 +28,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.91.0 WIP" -#define IMGUI_VERSION_NUM 19093 +#define IMGUI_VERSION_NUM 19094 #define IMGUI_HAS_TABLE /* @@ -1336,6 +1336,7 @@ enum ImGuiDataType_ ImGuiDataType_U64, // unsigned long long / unsigned __int64 ImGuiDataType_Float, // float ImGuiDataType_Double, // double + ImGuiDataType_Bool, // bool (provided for user convenience, not supported by scalar widgets) ImGuiDataType_COUNT }; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 558979b98..3ce3a2f2a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -2131,6 +2131,7 @@ static const ImGuiDataTypeInfo GDataTypeInfo[] = #endif { sizeof(float), "float", "%.3f","%f" }, // ImGuiDataType_Float (float are promoted to double in va_arg) { sizeof(double), "double","%f", "%lf" }, // ImGuiDataType_Double + { sizeof(bool), "bool", "%d", "%d" }, // ImGuiDataType_Bool }; IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT); From 46691d172ec65915b6a1e14dd9ae2c0a49dd15c4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Jul 2024 17:12:11 +0200 Subject: [PATCH 03/17] Demo: Reworked "Property Editor" demo in a manner that more ressemble the tree data and struct description data that a real application would want to use. --- docs/CHANGELOG.txt | 2 + imgui_demo.cpp | 182 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 141 insertions(+), 43 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c71adfca2..d5c58c1e7 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -62,6 +62,8 @@ Other changes: Debug log entries add a imgui frame counter prefix + are redirected to ShowDebugLogWindow() and other configurable locations. Always call IMGUI_DEBUG_LOG() for maximum stripping in caller code. - Debug Tools: Debug Log: Added "Configure Outputs.." button. (#5855) +- Demo: Reworked "Property Editor" demo in a manner that more ressemble the tree data and + struct description data that a real application would want to use. - Backends: Win32: Fixed ImGuiMod_Super being mapped to VK_APPS instead of VK_LWIN||VK_RWIN. (#7768, #4858, #2622) [@Aemony] - Backends: SDL3: Update for API changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 9da3cd965..019f67d9d 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -7749,53 +7749,162 @@ static void ShowExampleAppLayout(bool* p_open) // [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor() //----------------------------------------------------------------------------- -static void ShowPlaceholderObject(const char* prefix, int uid) +// Simple representation for a tree +// (this is designed to be simple to understand for our demos, not to be efficient etc.) +struct ExampleTreeNode { - // Use object uid as identifier. Most commonly you could also use the object pointer as a base ID. - ImGui::PushID(uid); + char Name[28]; + ImGuiID UID = 0; + ExampleTreeNode* Parent = NULL; + ImVector Childs; - // Text and Tree nodes are less high than framed widgets, using AlignTextToFramePadding() we add vertical spacing to make the tree lines equal high. + // Data + bool HasData = false; // All leaves have data + bool DataIsEnabled = false; + int DataInt = 128; + ImVec2 DataVec2 = ImVec2(0.0f, 3.141592f); +}; + +// Simple representation of struct metadata/serialization data. +// (this is a minimal version of what a typical advanced application may provide) +struct ExampleMemberInfo +{ + const char* Name; + ImGuiDataType DataType; + int DataCount; + int Offset; +}; + +// Metadata description of ExampleTreeNode struct. +static const ExampleMemberInfo ExampleTreeNodeMemberInfos[] +{ + { "Enabled", ImGuiDataType_Bool, 1, offsetof(ExampleTreeNode, DataIsEnabled) }, + { "MyInt", ImGuiDataType_S32, 1, offsetof(ExampleTreeNode, DataInt) }, + { "MyVec2", ImGuiDataType_Float, 2, offsetof(ExampleTreeNode, DataVec2) }, +}; + +static ExampleTreeNode* ExampleTree_CreateNode(const char* name, const ImGuiID uid, ExampleTreeNode* parent) +{ + ExampleTreeNode* node = IM_NEW(ExampleTreeNode); + snprintf(node->Name, IM_ARRAYSIZE(node->Name), "%s", name); + node->UID = uid; + node->Parent = parent; + if (parent) + parent->Childs.push_back(node); + return node; +} + +static ExampleTreeNode* ExampleTree_CreateDemoTree() +{ + static const char* root_names[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" }; + char name_buf[32]; + ImGuiID uid = 0; + ExampleTreeNode* node_L0 = ExampleTree_CreateNode("", ++uid, NULL); + for (int idx_L0 = 0; idx_L0 < IM_ARRAYSIZE(root_names) * 2; idx_L0++) + { + snprintf(name_buf, 32, "%s %d", root_names[idx_L0 / 2], idx_L0 % 2); + 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, 32, "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, 32, "Sub-child %d", 0); + ExampleTreeNode* node_L3 = ExampleTree_CreateNode(name_buf, ++uid, node_L2); + node_L3->HasData = true; + } + } + } + return node_L0; +} + +static void PropertyEditor_ShowTreeNode(ExampleTreeNode* node) +{ + // Object tree node + ImGui::PushID((int)node->UID); ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::AlignTextToFramePadding(); - bool node_open = ImGui::TreeNode("##Object", "%s_%u", prefix, uid); + ImGuiTreeNodeFlags tree_flags = ImGuiTreeNodeFlags_None; + tree_flags |= ImGuiTreeNodeFlags_SpanAllColumns | ImGuiTreeNodeFlags_AllowOverlap; // Highlight whole row for visibility + tree_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; // Standard opening mode as we are likely to want to add selection afterwards + tree_flags |= ImGuiTreeNodeFlags_NavLeftJumpsBackHere; // Left arrow support + bool node_open = ImGui::TreeNodeEx("##Object", tree_flags, "%s", node->Name); ImGui::TableSetColumnIndex(1); - ImGui::Text("my sailor is rich"); + ImGui::TextDisabled("UID: 0x%08X", node->UID); + // Display child and data if (node_open) + for (ExampleTreeNode* child : node->Childs) + PropertyEditor_ShowTreeNode(child); + if (node_open && node->HasData) { - static float placeholder_members[8] = { 0.0f, 0.0f, 1.0f, 3.1416f, 100.0f, 999.0f }; - for (int i = 0; i < 8; i++) + // In a typical application, the structure description would be derived from a data-driven system. + // - We try to mimic this with our ExampleMemberInfo structure and the ExampleTreeNodeMemberInfos[] array. + // - Limits and some details are hard-coded to simplify the demo. + // - Text and Selectable are less high than framed widgets, using AlignTextToFramePadding() we add vertical spacing to make the selectable lines equal high. + for (const ExampleMemberInfo& field_desc : ExampleTreeNodeMemberInfos) { - ImGui::PushID(i); // Use field index as identifier. - if (i < 2) + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::AlignTextToFramePadding(); + ImGui::PushTabStop(false); // We didn't expose ImGuiItemFlags_NoNav yet, so arrow navigation will still pass through this + ImGui::Selectable(field_desc.Name, false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap); + ImGui::PopTabStop(); + ImGui::TableSetColumnIndex(1); + ImGui::PushID(field_desc.Name); + void* field_ptr = (void*)(((unsigned char*)node) + field_desc.Offset); + switch (field_desc.DataType) { - ShowPlaceholderObject("Child", 424242); + case ImGuiDataType_Bool: + { + IM_ASSERT(field_desc.DataCount == 1); + ImGui::Checkbox("##Editor", (bool*)field_ptr); + break; } - else + case ImGuiDataType_S32: { - // Here we use a TreeNode to highlight on hover (we could use e.g. Selectable as well) - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::AlignTextToFramePadding(); - ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_Bullet; - ImGui::TreeNodeEx("Field", flags, "Field_%d", i); - - ImGui::TableSetColumnIndex(1); + int v_min = INT_MIN, v_max = INT_MAX; ImGui::SetNextItemWidth(-FLT_MIN); - if (i >= 5) - ImGui::InputFloat("##value", &placeholder_members[i], 1.0f); - else - ImGui::DragFloat("##value", &placeholder_members[i], 0.01f); - ImGui::NextColumn(); + ImGui::DragScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, 1.0f, &v_min, &v_max); + break; + } + case ImGuiDataType_Float: + { + float v_min = 0.0f, v_max = 1.0f; + ImGui::SetNextItemWidth(-FLT_MIN); + ImGui::SliderScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, &v_min, &v_max); + break; + } } ImGui::PopID(); } - ImGui::TreePop(); } + if (node_open) + ImGui::TreePop(); ImGui::PopID(); } +static void PropertyEditor_ShowTree(ExampleTreeNode* root_node) +{ + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2)); + ImGuiTableFlags table_flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg; + if (ImGui::BeginTable("##split", 2, table_flags)) + { + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableSetupColumn("Object", ImGuiTableColumnFlags_WidthStretch, 1.0f); + ImGui::TableSetupColumn("Contents", ImGuiTableColumnFlags_WidthStretch, 2.0f); // Default twice larger + ImGui::TableHeadersRow(); + for (ExampleTreeNode* node : root_node->Childs) + PropertyEditor_ShowTreeNode(node); + ImGui::EndTable(); + } + ImGui::PopStyleVar(); +} + // Demonstrate create a simple property editor. // This demo is a bit lackluster nowadays, would be nice to improve. static void ShowExampleAppPropertyEditor(bool* p_open) @@ -7808,25 +7917,12 @@ static void ShowExampleAppPropertyEditor(bool* p_open) } IMGUI_DEMO_MARKER("Examples/Property Editor"); - HelpMarker( - "This example shows how you may implement a property editor using two columns.\n" - "All objects/fields data are dummies here.\n"); + static ExampleTreeNode* tree_data = NULL; + if (tree_data == NULL) + tree_data = ExampleTree_CreateDemoTree(); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2)); - if (ImGui::BeginTable("##split", 2, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY)) - { - ImGui::TableSetupScrollFreeze(0, 1); - ImGui::TableSetupColumn("Object"); - ImGui::TableSetupColumn("Contents"); - ImGui::TableHeadersRow(); + PropertyEditor_ShowTree(tree_data); - // Iterate placeholder objects (all the same data) - for (int obj_i = 0; obj_i < 4; obj_i++) - ShowPlaceholderObject("Object", obj_i); - - ImGui::EndTable(); - } - ImGui::PopStyleVar(); ImGui::End(); } From 7e0800e718633c31e6e366df786aed50f46dec15 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Jul 2024 17:32:41 +0200 Subject: [PATCH 04/17] Added PushItemFlag(), PopItemFlag(), ImGuiItemFlags. --- docs/CHANGELOG.txt | 6 ++++++ imgui.h | 16 +++++++++++++++- imgui_demo.cpp | 4 ++-- imgui_internal.h | 30 ++++++++++-------------------- imgui_widgets.cpp | 2 +- 5 files changed, 34 insertions(+), 24 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d5c58c1e7..1ad1f4dd5 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -58,6 +58,12 @@ Other changes: - IO: added io.PlatformOpenInShellFn handler to open a link/folder/file in OS shell. (#7660) Default to use ShellExecute() under Windows, and system("") under Mac/Linux/etc. Added IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS to disable default implementation. +- Added PushItemFlag()/PopItemFlags(), ImGuiItemFlags to modify shared item flags: + - Added ImGuiItemFlags_NoTabStop to disable tabbing through items. + - Added ImGuiItemFlags_NoNav to disable any navigation and focus of items. (#787) + - Added ImGuiItemFlags_NoNavDefaultFocus to disable item being default focus. (#787) + - Added ImGuiItemFlags_ButtonRepeat to enable repeat on any button-like behavior. + - (previously in imgui_internal.h) - Debug Tools: Added IMGUI_DEBUG_LOG(), ImGui::DebugLog() in public API. (#5855) Debug log entries add a imgui frame counter prefix + are redirected to ShowDebugLogWindow() and other configurable locations. Always call IMGUI_DEBUG_LOG() for maximum stripping in caller code. diff --git a/imgui.h b/imgui.h index a2c2e8c08..2d4f251cd 100644 --- a/imgui.h +++ b/imgui.h @@ -224,6 +224,7 @@ typedef int ImGuiFocusedFlags; // -> enum ImGuiFocusedFlags_ // Flags: f typedef int ImGuiHoveredFlags; // -> enum ImGuiHoveredFlags_ // Flags: for IsItemHovered(), IsWindowHovered() etc. typedef int ImGuiInputFlags; // -> enum ImGuiInputFlags_ // Flags: for Shortcut(), SetNextItemShortcut() 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 ImGuiPopupFlags; // -> enum ImGuiPopupFlags_ // Flags: for OpenPopup*(), BeginPopupContext*(), IsPopupOpen() typedef int ImGuiSelectableFlags; // -> enum ImGuiSelectableFlags_ // Flags: for Selectable() @@ -436,9 +437,11 @@ namespace ImGui IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val); // modify a style float variable. always use this if you modify the style after NewFrame(). IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val); // modify a style ImVec2 variable. always use this if you modify the style after NewFrame(). IMGUI_API void PopStyleVar(int count = 1); + IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); // modify specified shared item flag, e.g. PushItemFlag(ImGuiItemFlags_NoTabStop, true) + IMGUI_API void PopItemFlag(); IMGUI_API void PushTabStop(bool tab_stop); // == tab stop enable. Allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets IMGUI_API void PopTabStop(); - IMGUI_API void PushButtonRepeat(bool repeat); // in 'repeat' mode, Button*() functions return repeated true in a typematic manner (using io.KeyRepeatDelay/io.KeyRepeatRate setting). Note that you can call IsItemActive() after any Button() to tell if the button is held in the current frame. + IMGUI_API void PushButtonRepeat(bool repeat); // in repeat mode, any button-like function behave with a repeating behavior (using io.KeyRepeatDelay/io.KeyRepeatRate values). Note that you can call IsItemActive() after any button to tell if it is being held. IMGUI_API void PopButtonRepeat(); // Parameters stacks (current window) @@ -1088,6 +1091,17 @@ enum ImGuiChildFlags_ ImGuiChildFlags_NavFlattened = 1 << 8, // Share focus scope, allow gamepad/keyboard navigation to cross over parent border to this child or between sibling child windows. }; +// Flags for ImGui::PushItemFlag() +// (Those are shared by all items) +enum ImGuiItemFlags_ +{ + ImGuiItemFlags_None = 0, // (Default) + ImGuiItemFlags_NoTabStop = 1 << 0, // false // Disable keyboard tabbing. This is a "lighter" version of ImGuiItemFlags_NoNav. + ImGuiItemFlags_NoNav = 1 << 1, // false // Disable any form of focusing (keyboard/gamepad directional navigation and SetKeyboardFocusHere() calls). + ImGuiItemFlags_NoNavDefaultFocus = 1 << 2, // false // Disable item being a candidate for default focus (e.g. used by title bar items). + ImGuiItemFlags_ButtonRepeat = 1 << 3, // false // Any button-like behavior will have repeat mode enabled (based on io.KeyRepeatDelay and io.KeyRepeatRate values). Note that you can also call IsItemActive() after any button to tell if it is being held. +}; + // Flags for ImGui::InputText() // (Those are per-item flags. There are shared flags in ImGuiIO: io.ConfigInputTextCursorBlink and io.ConfigInputTextEnterKeepActive) enum ImGuiInputTextFlags_ diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 019f67d9d..31debeff5 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -7851,9 +7851,9 @@ static void PropertyEditor_ShowTreeNode(ExampleTreeNode* node) ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::AlignTextToFramePadding(); - ImGui::PushTabStop(false); // We didn't expose ImGuiItemFlags_NoNav yet, so arrow navigation will still pass through this + ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop | ImGuiItemFlags_NoNav, true); ImGui::Selectable(field_desc.Name, false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap); - ImGui::PopTabStop(); + ImGui::PopItemFlag(); ImGui::TableSetColumnIndex(1); ImGui::PushID(field_desc.Name); void* field_ptr = (void*)(((unsigned char*)node) + field_desc.Offset); diff --git a/imgui_internal.h b/imgui_internal.h index 11ada4bab..0a46ee93e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -169,7 +169,6 @@ typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // E 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(); -typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag(), g.LastItemData.InFlags typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for g.LastItemData.StatusFlags typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns() typedef int ImGuiNavHighlightFlags; // -> enum ImGuiNavHighlightFlags_ // Flags: for RenderNavHighlight() @@ -840,29 +839,22 @@ enum ImGuiDataTypePrivate_ // [SECTION] Widgets support: flags, enums, data structures //----------------------------------------------------------------------------- -// Flags used by upcoming items +// Extend ImGuiItemFlags // - input: PushItemFlag() manipulates g.CurrentItemFlags, ItemAdd() calls may add extra flags. // - output: stored in g.LastItemData.InFlags -// Current window shared by all windows. -// This is going to be exposed in imgui.h when stabilized enough. -enum ImGuiItemFlags_ +enum ImGuiItemFlagsPrivate_ { // Controlled by user - ImGuiItemFlags_None = 0, - ImGuiItemFlags_NoTabStop = 1 << 0, // false // Disable keyboard tabbing. This is a "lighter" version of ImGuiItemFlags_NoNav. - ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings. - ImGuiItemFlags_Disabled = 1 << 2, // false // Disable interactions but doesn't affect visuals. See BeginDisabled()/EndDisabled(). See github.com/ocornut/imgui/issues/211 - ImGuiItemFlags_NoNav = 1 << 3, // false // Disable any form of focusing (keyboard/gamepad directional navigation and SetKeyboardFocusHere() calls) - ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false // Disable item being a candidate for default focus (e.g. used by title bar items) - ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // Disable MenuItem/Selectable() automatically closing their popup window - ImGuiItemFlags_MixedValue = 1 << 6, // 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_ReadOnly = 1 << 7, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. - ImGuiItemFlags_NoWindowHoverableCheck = 1 << 8, // false // Disable hoverable check in ItemHoverable() - ImGuiItemFlags_AllowOverlap = 1 << 9, // false // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame. + ImGuiItemFlags_Disabled = 1 << 10, // false // Disable interactions (DOES NOT affect visuals, see BeginDisabled()/EndDisabled() for full disable feature, and github #211). + ImGuiItemFlags_SelectableDontClosePopup = 1 << 11, // false // Disable MenuItem/Selectable() automatically closing their popup window + 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_ReadOnly = 1 << 13, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. + ImGuiItemFlags_NoWindowHoverableCheck = 1 << 14, // false // Disable hoverable check in ItemHoverable() + ImGuiItemFlags_AllowOverlap = 1 << 15, // false // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame. // Controlled by widget code - ImGuiItemFlags_Inputable = 1 << 10, // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature. - ImGuiItemFlags_HasSelectionUserData = 1 << 11, // false // Set by SetNextItemSelectionUserData() + ImGuiItemFlags_Inputable = 1 << 20, // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature. + ImGuiItemFlags_HasSelectionUserData = 1 << 21, // false // Set by SetNextItemSelectionUserData() }; // Status flags for an already submitted item @@ -3153,8 +3145,6 @@ namespace ImGui IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess); // Parameter stacks (shared) - IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); - IMGUI_API void PopItemFlag(); IMGUI_API const ImGuiDataVarInfo* GetStyleVarInfo(ImGuiStyleVar idx); IMGUI_API void BeginDisabledOverrideReenable(); IMGUI_API void EndDisabledOverrideReenable(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 3ce3a2f2a..6af70d96d 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6745,7 +6745,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl } const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0; - const bool item_add = ItemAdd(bb, id, NULL, disabled_item ? ImGuiItemFlags_Disabled : ImGuiItemFlags_None); + const bool item_add = ItemAdd(bb, id, NULL, disabled_item ? (ImGuiItemFlags_)ImGuiItemFlags_Disabled : ImGuiItemFlags_None); if (span_all_columns) { window->ClipRect.Min.x = backup_clip_rect_min_x; From b4ca869c407950d6eb26b9e683230ead674c438e Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Jul 2024 17:46:32 +0200 Subject: [PATCH 05/17] (Breaking) Obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag()/PopItemFlag() with ImGuiItemFlags_ButtonRepeat. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 11 +---------- imgui.h | 5 +++-- imgui_demo.cpp | 8 +++----- 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1ad1f4dd5..317e18efa 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -46,6 +46,8 @@ Breaking changes: - new: io.PlatformSetImeDataFn(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); It is expected that for a vast majority of users this is automatically set by core library and/or platform backend so it won't have any effect. +- Obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag()/PopItemFlag() + with ImGuiItemFlags_ButtonRepeat. Kept inline redirecting functions (will obsolete). - Commented out obsolete ImGuiModFlags (renamed to ImGuiKeyChord in 1.89). (#4921, #456) - Commented out obsolete ImGuiModFlags_XXX values (renamed to ImGuiMod_XXX in 1.89). (#4921, #456) - ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl, ImGuiModFlags_Shift -> ImGuiMod_Shift etc. diff --git a/imgui.cpp b/imgui.cpp index 0917c3092..beb6bc678 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -430,6 +430,7 @@ 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. + - 2024/07/15 (1.91.0) - obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag(ImGuiItemFlags_ButtonRepeat, ...)/PopItemFlag(). - 2024/07/02 (1.91.0) - commented out obsolete ImGuiModFlags (renamed to ImGuiKeyChord in 1.89). (#4921, #456) - commented out obsolete ImGuiModFlags_XXX values (renamed to ImGuiMod_XXX in 1.89). (#4921, #456) - ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl, ImGuiModFlags_Shift -> ImGuiMod_Shift etc. @@ -7630,16 +7631,6 @@ void ImGui::PopTabStop() PopItemFlag(); } -void ImGui::PushButtonRepeat(bool repeat) -{ - PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat); -} - -void ImGui::PopButtonRepeat() -{ - PopItemFlag(); -} - void ImGui::PushTextWrapPos(float wrap_pos_x) { ImGuiWindow* window = GetCurrentWindow(); diff --git a/imgui.h b/imgui.h index 2d4f251cd..e57535d6a 100644 --- a/imgui.h +++ b/imgui.h @@ -441,8 +441,6 @@ namespace ImGui IMGUI_API void PopItemFlag(); IMGUI_API void PushTabStop(bool tab_stop); // == tab stop enable. Allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets IMGUI_API void PopTabStop(); - IMGUI_API void PushButtonRepeat(bool repeat); // in repeat mode, any button-like function behave with a repeating behavior (using io.KeyRepeatDelay/io.KeyRepeatRate values). Note that you can call IsItemActive() after any button to tell if it is being held. - IMGUI_API void PopButtonRepeat(); // Parameters stacks (current window) IMGUI_API void PushItemWidth(float item_width); // push width of items for common large "item+label" widgets. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -FLT_MIN always align width to the right side). @@ -3314,6 +3312,9 @@ struct ImGuiPlatformImeData #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS namespace ImGui { + // OBSOLETED in 1.91.0 (from July 2024) + static inline void PushButtonRepeat(bool repeat) { PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat); } + static inline void PopButtonRepeat() { PopItemFlag(); } // 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(); } diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 31debeff5..c89ec38cf 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -662,11 +662,11 @@ static void ShowDemoWindowWidgets() IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Repeating)"); static int counter = 0; float spacing = ImGui::GetStyle().ItemInnerSpacing.x; - ImGui::PushButtonRepeat(true); + ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true); if (ImGui::ArrowButton("##left", ImGuiDir_Left)) { counter--; } ImGui::SameLine(0.0f, spacing); if (ImGui::ArrowButton("##right", ImGuiDir_Right)) { counter++; } - ImGui::PopButtonRepeat(); + ImGui::PopItemFlag(); ImGui::SameLine(); ImGui::Text("%d", counter); @@ -2581,7 +2581,7 @@ static void ShowDemoWindowWidgets() ImGui::BeginDisabled(true); if (item_type == 0) { ImGui::Text("ITEM: Text"); } // Testing text items with no identifier/interaction if (item_type == 1) { ret = ImGui::Button("ITEM: Button"); } // Testing button - if (item_type == 2) { ImGui::PushButtonRepeat(true); ret = ImGui::Button("ITEM: Button"); ImGui::PopButtonRepeat(); } // Testing button (with repeater) + 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) @@ -5875,7 +5875,6 @@ static void ShowDemoWindowTables() // Show data // FIXME-TABLE FIXME-NAV: How we can get decent up/down even though we have the buttons here? - ImGui::PushButtonRepeat(true); #if 1 // Demonstrate using clipper for large vertical lists ImGuiListClipper clipper; @@ -5960,7 +5959,6 @@ static void ShowDemoWindowTables() ImGui::PopID(); } } - ImGui::PopButtonRepeat(); // Store some info to display debug details below table_scroll_cur = ImVec2(ImGui::GetScrollX(), ImGui::GetScrollY()); From 0de88a928d7feb5c89d4b73c0fe373a6e16da394 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Jul 2024 17:58:43 +0200 Subject: [PATCH 06/17] Added ImGuiItemFlags_AutoClosePopups as a replacement for internal's ImGuiItemFlags_SelectableDontClosePopup. (#1379, #1468, #2200, #4936, #5216, #7302, #7573) --- docs/CHANGELOG.txt | 8 +++++++- imgui.cpp | 3 ++- imgui.h | 1 + imgui_internal.h | 12 ++++++++---- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 2 +- 6 files changed, 20 insertions(+), 8 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 317e18efa..584835e99 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -53,6 +53,9 @@ Breaking changes: - ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl, ImGuiModFlags_Shift -> ImGuiMod_Shift etc. - Backends: GLFW+Emscripten: Renamed ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback() to ImGui_ImplGlfw_InstallEmscriptenCallbacks(), with additional GLFWWindow* parameter. (#7647) [@ypujante] +- Internals: changed/inverted ImGuiItemFlags_SelectableDontClosePopup (default==false) to + ImGuiItemFlags_AutoClosePopups (default==true), same logic, only inverted behavior. + (#1379, #2200, #4936, #5216, #7302) Other changes: @@ -65,7 +68,10 @@ Other changes: - Added ImGuiItemFlags_NoNav to disable any navigation and focus of items. (#787) - Added ImGuiItemFlags_NoNavDefaultFocus to disable item being default focus. (#787) - Added ImGuiItemFlags_ButtonRepeat to enable repeat on any button-like behavior. - - (previously in imgui_internal.h) + - Added ImGuiItemFlags_AutoClosePopups to disable menu items/selection auto closing parent popups. + Disabling this was previously possible for Selectable() via a direct flag but not for MenuItem(). + (#1379, #1468, #2200, #4936, #5216, #7302, #7573) + - This was mostly all previously in imgui_internal.h. - Debug Tools: Added IMGUI_DEBUG_LOG(), ImGui::DebugLog() in public API. (#5855) Debug log entries add a imgui frame counter prefix + are redirected to ShowDebugLogWindow() and other configurable locations. Always call IMGUI_DEBUG_LOG() for maximum stripping in caller code. diff --git a/imgui.cpp b/imgui.cpp index beb6bc678..14d726caa 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4903,7 +4903,8 @@ void ImGui::NewFrame() g.CurrentWindowStack.resize(0); g.BeginPopupStack.resize(0); g.ItemFlagsStack.resize(0); - g.ItemFlagsStack.push_back(ImGuiItemFlags_None); + g.ItemFlagsStack.push_back(ImGuiItemFlags_AutoClosePopups); // Default flags + g.CurrentItemFlags = g.ItemFlagsStack.back(); g.GroupStack.resize(0); // [DEBUG] Update debug features diff --git a/imgui.h b/imgui.h index e57535d6a..dd5f9c6de 100644 --- a/imgui.h +++ b/imgui.h @@ -1098,6 +1098,7 @@ enum ImGuiItemFlags_ ImGuiItemFlags_NoNav = 1 << 1, // false // Disable any form of focusing (keyboard/gamepad directional navigation and SetKeyboardFocusHere() calls). ImGuiItemFlags_NoNavDefaultFocus = 1 << 2, // false // Disable item being a candidate for default focus (e.g. used by title bar items). ImGuiItemFlags_ButtonRepeat = 1 << 3, // false // Any button-like behavior will have repeat mode enabled (based on io.KeyRepeatDelay and io.KeyRepeatRate values). Note that you can also call IsItemActive() after any button to tell if it is being held. + ImGuiItemFlags_AutoClosePopups = 1 << 4, // true // MenuItem()/Selectable() automatically close their parent popup window. }; // Flags for ImGui::InputText() diff --git a/imgui_internal.h b/imgui_internal.h index 0a46ee93e..cefdb6ae9 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -846,15 +846,19 @@ enum ImGuiItemFlagsPrivate_ { // Controlled by user ImGuiItemFlags_Disabled = 1 << 10, // false // Disable interactions (DOES NOT affect visuals, see BeginDisabled()/EndDisabled() for full disable feature, and github #211). - ImGuiItemFlags_SelectableDontClosePopup = 1 << 11, // false // Disable MenuItem/Selectable() automatically closing their popup window + 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_ReadOnly = 1 << 13, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. - ImGuiItemFlags_NoWindowHoverableCheck = 1 << 14, // false // Disable hoverable check in ItemHoverable() - ImGuiItemFlags_AllowOverlap = 1 << 15, // false // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame. + ImGuiItemFlags_NoWindowHoverableCheck = 1 << 13, // false // Disable hoverable check in ItemHoverable() + ImGuiItemFlags_AllowOverlap = 1 << 14, // false // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame. // Controlled by widget code ImGuiItemFlags_Inputable = 1 << 20, // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature. ImGuiItemFlags_HasSelectionUserData = 1 << 21, // false // Set by SetNextItemSelectionUserData() + + ImGuiItemFlags_Default_ = ImGuiItemFlags_AutoClosePopups, // Please don't change, use PushItemFlag() instead. + + // Obsolete + //ImGuiItemFlags_SelectableDontClosePopup = !ImGuiItemFlags_AutoClosePopups, // Can't have a redirect as we inverted the behavior }; // Status flags for an already submitted item diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 70c74d43e..90131c83d 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -3471,7 +3471,7 @@ void ImGui::TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags Separator(); want_separator = true; - PushItemFlag(ImGuiItemFlags_SelectableDontClosePopup, true); + PushItemFlag(ImGuiItemFlags_AutoClosePopups, false); for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++) { ImGuiTableColumn* other_column = &table->Columns[other_column_n]; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 6af70d96d..ccef757ab 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6831,7 +6831,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb); // Automatically close popups - if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(g.LastItemData.InFlags & ImGuiItemFlags_SelectableDontClosePopup)) + if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && (g.LastItemData.InFlags & ImGuiItemFlags_AutoClosePopups)) CloseCurrentPopup(); if (disabled_item && !disabled_global) From 3de75138d1c3fda656957834fa2fe167c5ff6afa Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Jul 2024 18:14:33 +0200 Subject: [PATCH 07/17] (Breaking) Renamed ImGuiSelectableFlags_DontClosePopups to ImGuiSelectableFlags_NoAutoClosePopups. (#1379, #1468, #2200, #4936, #5216, #7302, #7573) --- docs/CHANGELOG.txt | 13 ++++++++----- imgui.cpp | 2 ++ imgui.h | 5 +++-- imgui_widgets.cpp | 4 ++-- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 584835e99..cf3b98f4e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -46,16 +46,19 @@ Breaking changes: - new: io.PlatformSetImeDataFn(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); It is expected that for a vast majority of users this is automatically set by core library and/or platform backend so it won't have any effect. -- Obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag()/PopItemFlag() - with ImGuiItemFlags_ButtonRepeat. Kept inline redirecting functions (will obsolete). +- Item flag changes: + - Obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag()/PopItemFlag() + with ImGuiItemFlags_ButtonRepeat. Kept inline redirecting functions (will obsolete). + - Renamed ImGuiSelectableFlags_DontClosePopups to ImGuiSelectableFlags_NoAutoClosePopups for + consistency. Kept inline redirecting functions (will obsolete). + + Internals: changed/inverted ImGuiItemFlags_SelectableDontClosePopup (default==false) to + ImGuiItemFlags_AutoClosePopups (default==true), same logic, only inverted behavior. + (#1379, #1468, #2200, #4936, #5216, #7302, #7573) - Commented out obsolete ImGuiModFlags (renamed to ImGuiKeyChord in 1.89). (#4921, #456) - Commented out obsolete ImGuiModFlags_XXX values (renamed to ImGuiMod_XXX in 1.89). (#4921, #456) - ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl, ImGuiModFlags_Shift -> ImGuiMod_Shift etc. - Backends: GLFW+Emscripten: Renamed ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback() to ImGui_ImplGlfw_InstallEmscriptenCallbacks(), with additional GLFWWindow* parameter. (#7647) [@ypujante] -- Internals: changed/inverted ImGuiItemFlags_SelectableDontClosePopup (default==false) to - ImGuiItemFlags_AutoClosePopups (default==true), same logic, only inverted behavior. - (#1379, #2200, #4936, #5216, #7302) Other changes: diff --git a/imgui.cpp b/imgui.cpp index 14d726caa..17871e855 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -430,6 +430,8 @@ 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. + - 2024/07/15 (1.91.0) - renamed ImGuiSelectableFlags_DontClosePopups to ImGuiSelectableFlags_NoAutoClosePopups. (#1379, #1468, #2200, #4936, #5216, #7302, #7573) + (internals: also renamed ImGuiItemFlags_SelectableDontClosePopup into ImGuiItemFlags_AutoClosePopups with inverted behaviors) - 2024/07/15 (1.91.0) - obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag(ImGuiItemFlags_ButtonRepeat, ...)/PopItemFlag(). - 2024/07/02 (1.91.0) - commented out obsolete ImGuiModFlags (renamed to ImGuiKeyChord in 1.89). (#4921, #456) - commented out obsolete ImGuiModFlags_XXX values (renamed to ImGuiMod_XXX in 1.89). (#4921, #456) diff --git a/imgui.h b/imgui.h index dd5f9c6de..aadd2d0ec 100644 --- a/imgui.h +++ b/imgui.h @@ -1198,14 +1198,15 @@ enum ImGuiPopupFlags_ enum ImGuiSelectableFlags_ { ImGuiSelectableFlags_None = 0, - ImGuiSelectableFlags_DontClosePopups = 1 << 0, // Clicking this doesn't close parent popup window + ImGuiSelectableFlags_NoAutoClosePopups = 1 << 0, // Clicking this doesn't close parent popup window (overrides ImGuiItemFlags_AutoClosePopups) ImGuiSelectableFlags_SpanAllColumns = 1 << 1, // Frame will span all columns of its container table (text will still fit in current column) ImGuiSelectableFlags_AllowDoubleClick = 1 << 2, // Generate press events on double clicks too 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 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - ImGuiSelectableFlags_AllowItemOverlap = ImGuiSelectableFlags_AllowOverlap, // Renamed in 1.89.7 + ImGuiSelectableFlags_DontClosePopups = ImGuiSelectableFlags_NoAutoClosePopups, // Renamed in 1.91.0 + ImGuiSelectableFlags_AllowItemOverlap = ImGuiSelectableFlags_AllowOverlap, // Renamed in 1.89.7 #endif }; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index ccef757ab..341da042c 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6831,7 +6831,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb); // Automatically close popups - if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && (g.LastItemData.InFlags & ImGuiItemFlags_AutoClosePopups)) + if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (g.LastItemData.InFlags & ImGuiItemFlags_AutoClosePopups)) CloseCurrentPopup(); if (disabled_item && !disabled_global) @@ -7659,7 +7659,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) bool pressed; // We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another. - const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups; + const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_NoAutoClosePopups; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { // Menu inside an horizontal menu bar From 0e4dcfa55299eebd4659181b3622d24566f2f764 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Jul 2024 18:41:06 +0200 Subject: [PATCH 08/17] Obsoleted PushTabStop()/PopTabStop() in favor of using new PushItemFlag()/PopItemFlag() with ImGuiItemFlags_NoTabStop. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 14 ++------------ imgui.h | 30 +++++++++++++++--------------- imgui_demo.cpp | 8 ++++---- 4 files changed, 23 insertions(+), 31 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index cf3b98f4e..56b4576ed 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -49,6 +49,8 @@ Breaking changes: - Item flag changes: - Obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag()/PopItemFlag() with ImGuiItemFlags_ButtonRepeat. Kept inline redirecting functions (will obsolete). + - Obsoleted PushTabStop()/PopTabStop() in favor of using new PushItemFlag()/PopItemFlag() + with ImGuiItemFlags_NoTabStop. Kept inline redirecting functions (will obsolete). - Renamed ImGuiSelectableFlags_DontClosePopups to ImGuiSelectableFlags_NoAutoClosePopups for consistency. Kept inline redirecting functions (will obsolete). + Internals: changed/inverted ImGuiItemFlags_SelectableDontClosePopup (default==false) to diff --git a/imgui.cpp b/imgui.cpp index 17871e855..605fae215 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7624,16 +7624,6 @@ void ImGui::EndDisabledOverrideReenable() g.Style.Alpha = g.DisabledAlphaBackup * g.Style.DisabledAlpha; } -void ImGui::PushTabStop(bool tab_stop) -{ - PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); -} - -void ImGui::PopTabStop() -{ - PopItemFlag(); -} - void ImGui::PushTextWrapPos(float wrap_pos_x) { ImGuiWindow* window = GetCurrentWindow(); @@ -13773,10 +13763,10 @@ void ImGui::LogButtons() #endif const bool log_to_file = Button("Log To File"); SameLine(); const bool log_to_clipboard = Button("Log To Clipboard"); SameLine(); - PushTabStop(false); + PushItemFlag(ImGuiItemFlags_NoTabStop, true); SetNextItemWidth(80.0f); SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL); - PopTabStop(); + PopItemFlag(); PopID(); // Start logging at the end of the function so that the buttons don't appear in the log diff --git a/imgui.h b/imgui.h index aadd2d0ec..0e657b46a 100644 --- a/imgui.h +++ b/imgui.h @@ -439,8 +439,6 @@ namespace ImGui IMGUI_API void PopStyleVar(int count = 1); IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); // modify specified shared item flag, e.g. PushItemFlag(ImGuiItemFlags_NoTabStop, true) IMGUI_API void PopItemFlag(); - IMGUI_API void PushTabStop(bool tab_stop); // == tab stop enable. Allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets - IMGUI_API void PopTabStop(); // Parameters stacks (current window) IMGUI_API void PushItemWidth(float item_width); // push width of items for common large "item+label" widgets. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -FLT_MIN always align width to the right side). @@ -3315,31 +3313,33 @@ struct ImGuiPlatformImeData namespace ImGui { // 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 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(); } // 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 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 border, ImGuiWindowFlags window_flags){ return BeginChild(str_id, size_arg, border ? ImGuiChildFlags_Border : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Border //static inline bool BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags window_flags) { return BeginChild(id, size_arg, border ? ImGuiChildFlags_Border : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Border - static inline void ShowStackToolWindow(bool* p_open = NULL) { ShowIDStackToolWindow(p_open); } - 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); + static 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. + IMGUI_API void SetItemAllowOverlap(); // Use SetNextItemAllowOverlap() before item. // OBSOLETED in 1.89.4 (from March 2023) - static inline void PushAllowKeyboardFocus(bool tab_stop) { PushTabStop(tab_stop); } - static inline void PopAllowKeyboardFocus() { PopTabStop(); } + static inline void PushAllowKeyboardFocus(bool tab_stop) { PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); } + static inline void PopAllowKeyboardFocus() { PopItemFlag(); } // OBSOLETED in 1.89 (from August 2022) IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); // Use new ImageButton() signature (explicit item id, regular FramePadding) // OBSOLETED in 1.87 (from February 2022 but more formally obsoleted April 2024) - IMGUI_API ImGuiKey GetKeyIndex(ImGuiKey key); // Map ImGuiKey_* values into legacy native key index. == io.KeyMap[key]. When using a 1.87+ backend using io.AddKeyEvent(), calling GetKeyIndex() with ANY ImGuiKey_XXXX values will return the same value! - //static inline ImGuiKey GetKeyIndex(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END); return key; } + IMGUI_API ImGuiKey GetKeyIndex(ImGuiKey key); // Map ImGuiKey_* values into legacy native key index. == io.KeyMap[key]. When using a 1.87+ backend using io.AddKeyEvent(), calling GetKeyIndex() with ANY ImGuiKey_XXXX values will return the same value! + //static inline ImGuiKey GetKeyIndex(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END); return key; } // Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE) //-- OBSOLETED in 1.88 (from May 2022) - //static inline void CaptureKeyboardFromApp(bool want_capture_keyboard = true) { SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } // Renamed as name was misleading + removed default value. - //static inline void CaptureMouseFromApp(bool want_capture_mouse = true) { SetNextFrameWantCaptureMouse(want_capture_mouse); } // Renamed as name was misleading + removed default value. + //static inline void CaptureKeyboardFromApp(bool want_capture_keyboard = true) { SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } // Renamed as name was misleading + removed default value. + //static inline void CaptureMouseFromApp(bool want_capture_mouse = true) { SetNextFrameWantCaptureMouse(want_capture_mouse); } // Renamed as name was misleading + removed default value. //-- OBSOLETED in 1.86 (from November 2021) //IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // Code removed, see 1.90 for last version of the code. Calculate range of visible items for large list of evenly sized items. Prefer using ImGuiListClipper. //-- OBSOLETED in 1.85 (from August 2021) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index c89ec38cf..dba63ee55 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -6421,10 +6421,10 @@ static void ShowDemoWindowInputs() ImGui::InputText("1", buf, IM_ARRAYSIZE(buf)); ImGui::InputText("2", buf, IM_ARRAYSIZE(buf)); ImGui::InputText("3", buf, IM_ARRAYSIZE(buf)); - ImGui::PushTabStop(false); + ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, true); ImGui::InputText("4 (tab skip)", buf, IM_ARRAYSIZE(buf)); ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab."); - ImGui::PopTabStop(); + ImGui::PopItemFlag(); ImGui::InputText("5", buf, IM_ARRAYSIZE(buf)); ImGui::TreePop(); } @@ -6446,12 +6446,12 @@ static void ShowDemoWindowInputs() ImGui::InputText("2", buf, IM_ARRAYSIZE(buf)); if (ImGui::IsItemActive()) has_focus = 2; - ImGui::PushTabStop(false); + ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, true); if (focus_3) ImGui::SetKeyboardFocusHere(); ImGui::InputText("3 (tab skip)", buf, IM_ARRAYSIZE(buf)); if (ImGui::IsItemActive()) has_focus = 3; ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab."); - ImGui::PopTabStop(); + ImGui::PopItemFlag(); if (has_focus) ImGui::Text("Item with focus: %d", has_focus); From 9c1f922b02bc53af6825e5e3f6e49519aedddc85 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 16 Jul 2024 17:19:12 +0200 Subject: [PATCH 09/17] Fixed pvs-studio warning. --- imgui_widgets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 341da042c..26968192b 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6745,7 +6745,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl } const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0; - const bool item_add = ItemAdd(bb, id, NULL, disabled_item ? (ImGuiItemFlags_)ImGuiItemFlags_Disabled : ImGuiItemFlags_None); + const bool item_add = ItemAdd(bb, id, NULL, disabled_item ? (ImGuiItemFlags)ImGuiItemFlags_Disabled : ImGuiItemFlags_None); if (span_all_columns) { window->ClipRect.Min.x = backup_clip_rect_min_x; From 4247f190c23325fbbf42c6452a0416ac2aebfcb0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 17 Jul 2024 13:41:59 +0200 Subject: [PATCH 10/17] Demo: Property Editor: rearrange code + replace use of bool to proper ImGuiChildFlags. Amend 46691d1 --- imgui_demo.cpp | 173 ++++++++++++++++++++++++---------------------- imgui_tables.cpp | 4 +- imgui_widgets.cpp | 2 +- 3 files changed, 92 insertions(+), 87 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index dba63ee55..96e7e0ba6 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -83,6 +83,7 @@ Index of this file: // [SECTION] Example App: Debug Console / ShowExampleAppConsole() // [SECTION] Example App: Debug Log / ShowExampleAppLog() // [SECTION] Example App: Simple Layout / ShowExampleAppLayout() +// [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor etc.) // [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor() // [SECTION] Example App: Long Text / ShowExampleAppLongText() // [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize() @@ -7744,7 +7745,7 @@ static void ShowExampleAppLayout(bool* p_open) } //----------------------------------------------------------------------------- -// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor() +// [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor etc.) //----------------------------------------------------------------------------- // Simple representation for a tree @@ -7819,92 +7820,98 @@ static ExampleTreeNode* ExampleTree_CreateDemoTree() return node_L0; } -static void PropertyEditor_ShowTreeNode(ExampleTreeNode* node) -{ - // Object tree node - ImGui::PushID((int)node->UID); - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::AlignTextToFramePadding(); - ImGuiTreeNodeFlags tree_flags = ImGuiTreeNodeFlags_None; - tree_flags |= ImGuiTreeNodeFlags_SpanAllColumns | ImGuiTreeNodeFlags_AllowOverlap; // Highlight whole row for visibility - tree_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; // Standard opening mode as we are likely to want to add selection afterwards - tree_flags |= ImGuiTreeNodeFlags_NavLeftJumpsBackHere; // Left arrow support - bool node_open = ImGui::TreeNodeEx("##Object", tree_flags, "%s", node->Name); - ImGui::TableSetColumnIndex(1); - ImGui::TextDisabled("UID: 0x%08X", node->UID); +//----------------------------------------------------------------------------- +// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor() +//----------------------------------------------------------------------------- - // Display child and data - if (node_open) - for (ExampleTreeNode* child : node->Childs) - PropertyEditor_ShowTreeNode(child); - if (node_open && node->HasData) +struct ExampleAppPropertyEditor +{ + void Draw(ExampleTreeNode* root_node) { - // In a typical application, the structure description would be derived from a data-driven system. - // - We try to mimic this with our ExampleMemberInfo structure and the ExampleTreeNodeMemberInfos[] array. - // - Limits and some details are hard-coded to simplify the demo. - // - Text and Selectable are less high than framed widgets, using AlignTextToFramePadding() we add vertical spacing to make the selectable lines equal high. - for (const ExampleMemberInfo& field_desc : ExampleTreeNodeMemberInfos) + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2)); + ImGuiTableFlags table_flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg; + if (ImGui::BeginTable("##split", 2, table_flags)) { - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::AlignTextToFramePadding(); - ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop | ImGuiItemFlags_NoNav, true); - ImGui::Selectable(field_desc.Name, false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap); - ImGui::PopItemFlag(); - ImGui::TableSetColumnIndex(1); - ImGui::PushID(field_desc.Name); - void* field_ptr = (void*)(((unsigned char*)node) + field_desc.Offset); - switch (field_desc.DataType) - { - case ImGuiDataType_Bool: - { - IM_ASSERT(field_desc.DataCount == 1); - ImGui::Checkbox("##Editor", (bool*)field_ptr); - break; - } - case ImGuiDataType_S32: - { - int v_min = INT_MIN, v_max = INT_MAX; - ImGui::SetNextItemWidth(-FLT_MIN); - ImGui::DragScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, 1.0f, &v_min, &v_max); - break; - } - case ImGuiDataType_Float: - { - float v_min = 0.0f, v_max = 1.0f; - ImGui::SetNextItemWidth(-FLT_MIN); - ImGui::SliderScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, &v_min, &v_max); - break; - } - } - ImGui::PopID(); + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableSetupColumn("Object", ImGuiTableColumnFlags_WidthStretch, 1.0f); + ImGui::TableSetupColumn("Contents", ImGuiTableColumnFlags_WidthStretch, 2.0f); // Default twice larger + ImGui::TableHeadersRow(); + for (ExampleTreeNode* node : root_node->Childs) + DrawTreeNode(node); + ImGui::EndTable(); } + ImGui::PopStyleVar(); } - if (node_open) - ImGui::TreePop(); - ImGui::PopID(); -} -static void PropertyEditor_ShowTree(ExampleTreeNode* root_node) -{ - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2)); - ImGuiTableFlags table_flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg; - if (ImGui::BeginTable("##split", 2, table_flags)) + void DrawTreeNode(ExampleTreeNode* node) { - ImGui::TableSetupScrollFreeze(0, 1); - ImGui::TableSetupColumn("Object", ImGuiTableColumnFlags_WidthStretch, 1.0f); - ImGui::TableSetupColumn("Contents", ImGuiTableColumnFlags_WidthStretch, 2.0f); // Default twice larger - ImGui::TableHeadersRow(); - for (ExampleTreeNode* node : root_node->Childs) - PropertyEditor_ShowTreeNode(node); - ImGui::EndTable(); - } - ImGui::PopStyleVar(); -} + // Object tree node + ImGui::PushID((int)node->UID); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::AlignTextToFramePadding(); + ImGuiTreeNodeFlags tree_flags = ImGuiTreeNodeFlags_None; + tree_flags |= ImGuiTreeNodeFlags_SpanAllColumns | ImGuiTreeNodeFlags_AllowOverlap; // Highlight whole row for visibility + tree_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; // Standard opening mode as we are likely to want to add selection afterwards + tree_flags |= ImGuiTreeNodeFlags_NavLeftJumpsBackHere; // Left arrow support + bool node_open = ImGui::TreeNodeEx("##Object", tree_flags, "%s", node->Name); + ImGui::TableSetColumnIndex(1); + ImGui::TextDisabled("UID: 0x%08X", node->UID); -// Demonstrate create a simple property editor. -// This demo is a bit lackluster nowadays, would be nice to improve. + // Display child and data + if (node_open) + for (ExampleTreeNode* child : node->Childs) + DrawTreeNode(child); + if (node_open && node->HasData) + { + // In a typical application, the structure description would be derived from a data-driven system. + // - We try to mimic this with our ExampleMemberInfo structure and the ExampleTreeNodeMemberInfos[] array. + // - Limits and some details are hard-coded to simplify the demo. + // - Text and Selectable are less high than framed widgets, using AlignTextToFramePadding() we add vertical spacing to make the selectable lines equal high. + for (const ExampleMemberInfo& field_desc : ExampleTreeNodeMemberInfos) + { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::AlignTextToFramePadding(); + ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop | ImGuiItemFlags_NoNav, true); + ImGui::Selectable(field_desc.Name, false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap); + ImGui::PopItemFlag(); + ImGui::TableSetColumnIndex(1); + ImGui::PushID(field_desc.Name); + void* field_ptr = (void*)(((unsigned char*)node) + field_desc.Offset); + switch (field_desc.DataType) + { + case ImGuiDataType_Bool: + { + IM_ASSERT(field_desc.DataCount == 1); + ImGui::Checkbox("##Editor", (bool*)field_ptr); + break; + } + case ImGuiDataType_S32: + { + int v_min = INT_MIN, v_max = INT_MAX; + ImGui::SetNextItemWidth(-FLT_MIN); + ImGui::DragScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, 1.0f, &v_min, &v_max); + break; + } + case ImGuiDataType_Float: + { + float v_min = 0.0f, v_max = 1.0f; + ImGui::SetNextItemWidth(-FLT_MIN); + ImGui::SliderScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, &v_min, &v_max); + break; + } + } + ImGui::PopID(); + } + } + if (node_open) + ImGui::TreePop(); + ImGui::PopID(); + } +}; + +// Demonstrate creating a simple property editor. static void ShowExampleAppPropertyEditor(bool* p_open) { ImGui::SetNextWindowSize(ImVec2(430, 450), ImGuiCond_FirstUseEver); @@ -7915,11 +7922,9 @@ static void ShowExampleAppPropertyEditor(bool* p_open) } IMGUI_DEMO_MARKER("Examples/Property Editor"); - static ExampleTreeNode* tree_data = NULL; - if (tree_data == NULL) - tree_data = ExampleTree_CreateDemoTree(); - - PropertyEditor_ShowTree(tree_data); + static ExampleAppPropertyEditor property_editor; + static ExampleTreeNode* tree_data = ExampleTree_CreateDemoTree(); + property_editor.Draw(tree_data); ImGui::End(); } diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 90131c83d..ce6fed1ea 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -412,8 +412,8 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG SetNextWindowScroll(ImVec2(0.0f, 0.0f)); // Create scrolling region (without border and zero window padding) - ImGuiWindowFlags child_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None; - BeginChildEx(name, instance_id, outer_rect.GetSize(), false, child_flags); + ImGuiWindowFlags child_window_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None; + BeginChildEx(name, instance_id, outer_rect.GetSize(), ImGuiChildFlags_None, child_window_flags); table->InnerWindow = g.CurrentWindow; table->WorkRect = table->InnerWindow->WorkRect; table->OuterRect = table->InnerWindow->Rect(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 26968192b..be3d41a37 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4284,7 +4284,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding); PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize); PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Ensure no clip rect so mouse hover can reach FramePadding edges - bool child_visible = BeginChildEx(label, id, frame_bb.GetSize(), true, ImGuiWindowFlags_NoMove); + bool child_visible = BeginChildEx(label, id, frame_bb.GetSize(), ImGuiChildFlags_Border, ImGuiWindowFlags_NoMove); g.NavActivateId = backup_activate_id; PopStyleVar(3); PopStyleColor(); From 67e9aa4d3df8922dd27978eb7e93654785a0e339 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 17 Jul 2024 14:01:02 +0200 Subject: [PATCH 11/17] Demo: Property Editor: add basic filter. --- imgui_demo.cpp | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 96e7e0ba6..8f16e6c31 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -7823,24 +7823,40 @@ static ExampleTreeNode* ExampleTree_CreateDemoTree() //----------------------------------------------------------------------------- // [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor() //----------------------------------------------------------------------------- +// Some of the interactions are a bit lack-luster: +// - We would want the table scrolling window to use NavFlattened. +// - We would want pressing validating or leaving the filter to somehow restore focus. +//----------------------------------------------------------------------------- struct ExampleAppPropertyEditor { + ImGuiTextFilter Filter; + void Draw(ExampleTreeNode* root_node) { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2)); - ImGuiTableFlags table_flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg; + ImGui::SetNextItemWidth(-FLT_MIN); + ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F); + ImGui::PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true); + if (ImGui::InputTextWithHint("##Filter", "incl,-excl", Filter.InputBuf, IM_ARRAYSIZE(Filter.InputBuf), ImGuiInputTextFlags_EscapeClearsAll)) + Filter.Build(); + ImGui::PopItemFlag(); + + //ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2)); + ImGuiTableFlags table_flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg; if (ImGui::BeginTable("##split", 2, table_flags)) { - ImGui::TableSetupScrollFreeze(0, 1); ImGui::TableSetupColumn("Object", ImGuiTableColumnFlags_WidthStretch, 1.0f); ImGui::TableSetupColumn("Contents", ImGuiTableColumnFlags_WidthStretch, 2.0f); // Default twice larger - ImGui::TableHeadersRow(); + //ImGui::TableSetupScrollFreeze(0, 1); + //ImGui::TableHeadersRow(); + + // Filter root node for (ExampleTreeNode* node : root_node->Childs) - DrawTreeNode(node); + if (Filter.PassFilter(node->Name)) + DrawTreeNode(node); ImGui::EndTable(); } - ImGui::PopStyleVar(); + //ImGui::PopStyleVar(); } void DrawTreeNode(ExampleTreeNode* node) From 669021be4cde053edb8c0ee5f3d247fdb265d1aa Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 17 Jul 2024 15:23:53 +0200 Subject: [PATCH 12/17] Style: close button and collapse/window-menu button hover highlight made rectangular instead of round. The reason they were round in the first place was to work better with rounded windows/frames. However since the 4a8142449 rework #6749 we can naturally use a tigher bounding box and it seems to work ok either way. --- docs/CHANGELOG.txt | 1 + imgui_widgets.cpp | 19 +++++++++---------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 56b4576ed..ea3b649ab 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -77,6 +77,7 @@ Other changes: Disabling this was previously possible for Selectable() via a direct flag but not for MenuItem(). (#1379, #1468, #2200, #4936, #5216, #7302, #7573) - This was mostly all previously in imgui_internal.h. +- Style: close button and collapse/window-menu button hover highlight made rectangular instead of round. - Debug Tools: Added IMGUI_DEBUG_LOG(), ImGui::DebugLog() in public API. (#5855) Debug log entries add a imgui frame counter prefix + are redirected to ShowDebugLogWindow() and other configurable locations. Always call IMGUI_DEBUG_LOG() for maximum stripping in caller code. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index be3d41a37..702c5ea98 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -835,17 +835,15 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos) return pressed; // Render - // FIXME: Clarify this mess - ImU32 col = GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered); - ImVec2 center = bb.GetCenter(); + ImU32 bg_col = GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered); if (hovered) - window->DrawList->AddCircleFilled(center, ImMax(2.0f, g.FontSize * 0.5f + 1.0f), col); - - float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f; + window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col); + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_Compact); ImU32 cross_col = GetColorU32(ImGuiCol_Text); - center -= ImVec2(0.5f, 0.5f); - window->DrawList->AddLine(center + ImVec2(+cross_extent, +cross_extent), center + ImVec2(-cross_extent, -cross_extent), cross_col, 1.0f); - window->DrawList->AddLine(center + ImVec2(+cross_extent, -cross_extent), center + ImVec2(-cross_extent, +cross_extent), cross_col, 1.0f); + ImVec2 cross_center = bb.GetCenter() - ImVec2(0.5f, 0.5f); + float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f; + window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, +cross_extent), cross_center + ImVec2(-cross_extent, -cross_extent), cross_col, 1.0f); + window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, -cross_extent), cross_center + ImVec2(-cross_extent, +cross_extent), cross_col, 1.0f); return pressed; } @@ -866,7 +864,8 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos) ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); ImU32 text_col = GetColorU32(ImGuiCol_Text); if (hovered || held) - window->DrawList->AddCircleFilled(bb.GetCenter() + ImVec2(0.0f, -0.5f), g.FontSize * 0.5f + 1.0f, bg_col); + window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col); + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_Compact); RenderArrow(window->DrawList, bb.Min, text_col, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f); // Switch to moving the window after mouse is moved beyond the initial drag threshold From 74a1854db9a21d83f1c91681eb295ee05129a91f Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 17 Jul 2024 15:56:45 +0200 Subject: [PATCH 13/17] Nav, Demo: comments. --- imgui.cpp | 5 ++++- imgui_demo.cpp | 9 ++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 605fae215..0f64254f2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1096,7 +1096,7 @@ CODE #endif // Debug options -#define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL +#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 // 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. @@ -11716,6 +11716,9 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result) if (dbx != 0.0f || dby != 0.0f) { // For non-overlapping boxes, use distance between boxes + // FIXME-NAV: Quadrant may be incorrect because of (1) dbx bias and (2) curr.Max.y bias applied by NavBiasScoringRect() where typically curr.Max.y==curr.Min.y + // One typical case where this happens, with style.WindowMenuButtonPosition == ImGuiDir_Right, pressing Left to navigate from Close to Collapse tends to fail. + // Also see #6344. Calling ImGetDirQuadrantFromDelta() with unbiased values may be good but side-effects are plenty. dax = dbx; day = dby; dist_axial = dist_box; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 8f16e6c31..dd253454f 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -7793,6 +7793,7 @@ static ExampleTreeNode* ExampleTree_CreateNode(const char* name, const ImGuiID u return node; } +// Create example tree data static ExampleTreeNode* ExampleTree_CreateDemoTree() { static const char* root_names[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" }; @@ -7826,6 +7827,7 @@ static ExampleTreeNode* ExampleTree_CreateDemoTree() // Some of the interactions are a bit lack-luster: // - We would want the table scrolling window to use NavFlattened. // - We would want pressing validating or leaving the filter to somehow restore focus. +// - We may want more advanced filtering (child nodes) and clipper support: both will need extra work. //----------------------------------------------------------------------------- struct ExampleAppPropertyEditor @@ -7835,13 +7837,12 @@ struct ExampleAppPropertyEditor void Draw(ExampleTreeNode* root_node) { ImGui::SetNextItemWidth(-FLT_MIN); - ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F); + 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)) Filter.Build(); ImGui::PopItemFlag(); - //ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2)); ImGuiTableFlags table_flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg; if (ImGui::BeginTable("##split", 2, table_flags)) { @@ -7850,13 +7851,11 @@ struct ExampleAppPropertyEditor //ImGui::TableSetupScrollFreeze(0, 1); //ImGui::TableHeadersRow(); - // Filter root node for (ExampleTreeNode* node : root_node->Childs) - if (Filter.PassFilter(node->Name)) + if (Filter.PassFilter(node->Name)) // Filter root node DrawTreeNode(node); ImGui::EndTable(); } - //ImGui::PopStyleVar(); } void DrawTreeNode(ExampleTreeNode* node) From 8bab3eab6a86bfc277abec966fd3c591bf8fd13f Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 17 Jul 2024 18:14:01 +0200 Subject: [PATCH 14/17] Clipper: added SeekCursorForItem() function, for use when using ImGuiListClipper::Begin(INT_MAX). (#1311) Tagging #3609 just in case we made a mistake introducing a regression (but tests are passing and have been extended). --- docs/CHANGELOG.txt | 4 ++++ imgui.cpp | 30 ++++++++++++++++++------------ imgui.h | 10 ++++++++-- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index ea3b649ab..1bb5efa53 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -77,6 +77,10 @@ Other changes: Disabling this was previously possible for Selectable() via a direct flag but not for MenuItem(). (#1379, #1468, #2200, #4936, #5216, #7302, #7573) - This was mostly all previously in imgui_internal.h. +- Clipper: added SeekCursorForItem() function. When using ImGuiListClipper::Begin(INT_MAX) you can + can use the clipper without knowing the amount of items beforehand. (#1311) + In this situation, call ImGuiListClipper::SeekCursorForItem(items_count) as the end of your iteration + loop to position the layout cursor correctly. This is done automatically if provided a count to Begin(). - Style: close button and collapse/window-menu button hover highlight made rectangular instead of round. - Debug Tools: Added IMGUI_DEBUG_LOG(), ImGui::DebugLog() in public API. (#5855) Debug log entries add a imgui frame counter prefix + are redirected to ShowDebugLogWindow() and diff --git a/imgui.cpp b/imgui.cpp index 0f64254f2..733a5dfe1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2911,15 +2911,6 @@ static void ImGuiListClipper_SeekCursorAndSetupPrevLine(float pos_y, float line_ } } -static void ImGuiListClipper_SeekCursorForItem(ImGuiListClipper* clipper, int item_n) -{ - // StartPosY starts from ItemsFrozen hence the subtraction - // Perform the add and multiply with double to allow seeking through larger ranges - ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData; - float pos_y = (float)((double)clipper->StartPosY + data->LossynessOffset + (double)(item_n - data->ItemsFrozen) * clipper->ItemsHeight); - ImGuiListClipper_SeekCursorAndSetupPrevLine(pos_y, clipper->ItemsHeight); -} - ImGuiListClipper::ImGuiListClipper() { memset(this, 0, sizeof(*this)); @@ -2956,6 +2947,7 @@ void ImGuiListClipper::Begin(int items_count, float items_height) data->Reset(this); data->LossynessOffset = window->DC.CursorStartPosLossyness.y; TempData = data; + StartSeekOffsetY = data->LossynessOffset; } void ImGuiListClipper::End() @@ -2966,7 +2958,7 @@ void ImGuiListClipper::End() ImGuiContext& g = *Ctx; IMGUI_DEBUG_LOG_CLIPPER("Clipper: End() in '%s'\n", g.CurrentWindow->Name); if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0) - ImGuiListClipper_SeekCursorForItem(this, ItemsCount); + SeekCursorForItem(ItemsCount); // Restore temporary buffer and fix back pointers which may be invalidated when nesting IM_ASSERT(data->ListClipper == this); @@ -2990,6 +2982,17 @@ void ImGuiListClipper::IncludeItemsByIndex(int item_begin, int item_end) data->Ranges.push_back(ImGuiListClipperRange::FromIndices(item_begin, item_end)); } +// This is already called while stepping. +// The ONLY reason you may want to call this is if you passed INT_MAX to ImGuiListClipper::Begin() because you couldn't step item count beforehand. +void ImGuiListClipper::SeekCursorForItem(int item_n) +{ + // - Perform the add and multiply with double to allow seeking through larger ranges. + // - 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); +} + static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) { ImGuiContext& g = *clipper->Ctx; @@ -3053,6 +3056,9 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) const int already_submitted = clipper->DisplayEnd; if (calc_clipping) { + // Record seek offset, this is so ImGuiListClipper::Seek() can be called after ImGuiListClipperData is done + clipper->StartSeekOffsetY = (double)data->LossynessOffset - data->ItemsFrozen * (double)clipper->ItemsHeight; + if (g.LogEnabled) { // If logging is active, do not perform any clipping @@ -3100,7 +3106,7 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) clipper->DisplayStart = ImMax(data->Ranges[data->StepNo].Min, already_submitted); clipper->DisplayEnd = ImMin(data->Ranges[data->StepNo].Max, clipper->ItemsCount); if (clipper->DisplayStart > already_submitted) //-V1051 - ImGuiListClipper_SeekCursorForItem(clipper, clipper->DisplayStart); + clipper->SeekCursorForItem(clipper->DisplayStart); data->StepNo++; if (clipper->DisplayStart == clipper->DisplayEnd && data->StepNo < data->Ranges.Size) continue; @@ -3110,7 +3116,7 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) // After the last step: Let the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), // Advance the cursor to the end of the list and then returns 'false' to end the loop. if (clipper->ItemsCount < INT_MAX) - ImGuiListClipper_SeekCursorForItem(clipper, clipper->ItemsCount); + clipper->SeekCursorForItem(clipper->ItemsCount); return false; } diff --git a/imgui.h b/imgui.h index 0e657b46a..26965fd86 100644 --- a/imgui.h +++ b/imgui.h @@ -28,7 +28,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.91.0 WIP" -#define IMGUI_VERSION_NUM 19094 +#define IMGUI_VERSION_NUM 19095 #define IMGUI_HAS_TABLE /* @@ -2601,9 +2601,10 @@ struct ImGuiListClipper int ItemsCount; // [Internal] Number of items float ItemsHeight; // [Internal] Height of item after a first step and item submission can calculate it float 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 - // 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) + // 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(). IMGUI_API ImGuiListClipper(); IMGUI_API ~ImGuiListClipper(); @@ -2616,6 +2617,11 @@ struct ImGuiListClipper inline void IncludeItemByIndex(int item_index) { IncludeItemsByIndex(item_index, item_index + 1); } IMGUI_API void IncludeItemsByIndex(int item_begin, int item_end); // item_end is exclusive e.g. use (42, 42+1) to make item 42 never clipped. + // Seek cursor toward given item. This is automatically called while stepping. + // - The only reason to call this is: you can use ImGuiListClipper::Begin(INT_MAX) if you don't know item count ahead of time. + // - In this case, after all steps are done, you'll want to call SeekCursorForItem(item_count). + 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 ForceDisplayRangeByIndices(int item_begin, int item_end) { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.6] From 7c6d4ff28de462e4bc1ead3b450dfb3eb72a7b93 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 17 Jul 2024 19:19:52 +0200 Subject: [PATCH 15/17] TreeNode: Internals: facilitate dissociating item ID from storage ID (useful for 1861) --- imgui_internal.h | 12 +++++++----- imgui_widgets.cpp | 44 ++++++++++++++++++++++++-------------------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index cefdb6ae9..4523a0228 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3461,13 +3461,15 @@ namespace ImGui IMGUI_API bool DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags); IMGUI_API bool SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb); IMGUI_API bool SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f, ImU32 bg_col = 0); - IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); - IMGUI_API void TreePushOverrideID(ImGuiID id); - IMGUI_API bool TreeNodeIsOpen(ImGuiID id); - IMGUI_API void TreeNodeSetOpen(ImGuiID id, bool open); - IMGUI_API bool TreeNodeUpdateNextOpen(ImGuiID id, ImGuiTreeNodeFlags flags); // Return open state. Consume previous SetNextItemOpen() data, if any. May return true when logging. IMGUI_API void SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data); + // Widgets: Tree Nodes + IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); + IMGUI_API void TreePushOverrideID(ImGuiID id); + IMGUI_API bool TreeNodeIsOpen(ImGuiID storage_id); + IMGUI_API void TreeNodeSetOpen(ImGuiID storage_id, bool open); + IMGUI_API bool TreeNodeUpdateNextOpen(ImGuiID storage_id, ImGuiTreeNodeFlags flags); // Return open state. Consume previous SetNextItemOpen() data, if any. May return true when logging. + // Template functions are instantiated in imgui_widgets.cpp for a finite number of types. // To use them externally (for custom widget) you may need an "extern template" statement in your code in order to link to existing instances and silence Clang warnings (see #2036). // e.g. " extern template IMGUI_API float RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, float v); " diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 702c5ea98..6e0b5d990 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6195,7 +6195,8 @@ bool ImGui::TreeNode(const char* label) ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; - return TreeNodeBehavior(window->GetID(label), 0, label, NULL); + ImGuiID id = window->GetID(label); + return TreeNodeBehavior(id, id, ImGuiTreeNodeFlags_None, label, NULL); } bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args) @@ -6213,8 +6214,8 @@ bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags) ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; - - return TreeNodeBehavior(window->GetID(label), flags, label, NULL); + ImGuiID id = window->GetID(label); + return TreeNodeBehavior(id, id, flags, label, NULL); } bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) @@ -6241,9 +6242,10 @@ bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char if (window->SkipItems) return false; + ImGuiID id = window->GetID(str_id); const char* label, *label_end; ImFormatStringToTempBufferV(&label, &label_end, fmt, args); - return TreeNodeBehavior(window->GetID(str_id), flags, label, label_end); + return TreeNodeBehavior(id, id, flags, label, label_end); } bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) @@ -6252,26 +6254,27 @@ bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char if (window->SkipItems) return false; + ImGuiID id = window->GetID(ptr_id); const char* label, *label_end; ImFormatStringToTempBufferV(&label, &label_end, fmt, args); - return TreeNodeBehavior(window->GetID(ptr_id), flags, label, label_end); + return TreeNodeBehavior(id, id, flags, label, label_end); } -bool ImGui::TreeNodeIsOpen(ImGuiID id) +bool ImGui::TreeNodeIsOpen(ImGuiID storage_id) { ImGuiContext& g = *GImGui; ImGuiStorage* storage = g.CurrentWindow->DC.StateStorage; - return storage->GetInt(id, 0) != 0; + return storage->GetInt(storage_id, 0) != 0; } -void ImGui::TreeNodeSetOpen(ImGuiID id, bool open) +void ImGui::TreeNodeSetOpen(ImGuiID storage_id, bool open) { ImGuiContext& g = *GImGui; ImGuiStorage* storage = g.CurrentWindow->DC.StateStorage; - storage->SetInt(id, open ? 1 : 0); + storage->SetInt(storage_id, open ? 1 : 0); } -bool ImGui::TreeNodeUpdateNextOpen(ImGuiID id, ImGuiTreeNodeFlags flags) +bool ImGui::TreeNodeUpdateNextOpen(ImGuiID storage_id, ImGuiTreeNodeFlags flags) { if (flags & ImGuiTreeNodeFlags_Leaf) return true; @@ -6287,16 +6290,16 @@ bool ImGui::TreeNodeUpdateNextOpen(ImGuiID id, ImGuiTreeNodeFlags flags) if (g.NextItemData.OpenCond & ImGuiCond_Always) { is_open = g.NextItemData.OpenVal; - TreeNodeSetOpen(id, is_open); + TreeNodeSetOpen(storage_id, is_open); } else { // We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently. - const int stored_value = storage->GetInt(id, -1); + const int stored_value = storage->GetInt(storage_id, -1); if (stored_value == -1) { is_open = g.NextItemData.OpenVal; - TreeNodeSetOpen(id, is_open); + TreeNodeSetOpen(storage_id, is_open); } else { @@ -6306,7 +6309,7 @@ bool ImGui::TreeNodeUpdateNextOpen(ImGuiID id, ImGuiTreeNodeFlags flags) } else { - is_open = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0; + is_open = storage->GetInt(storage_id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0; } // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior). @@ -6333,7 +6336,8 @@ static void TreeNodeStoreStackData(ImGuiTreeNodeFlags flags) window->DC.TreeHasStackDataDepthMask |= (1 << window->DC.TreeDepth); } -bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end) +// 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, ImGuiID storage_id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -6386,7 +6390,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l } // Compute open and multi-select states before ItemAdd() as it clear NextItem data. - bool is_open = TreeNodeUpdateNextOpen(id, flags); + bool is_open = TreeNodeUpdateNextOpen(storage_id, flags); bool item_add = ItemAdd(interact_bb, id); g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; g.LastItemData.DisplayRect = frame_bb; @@ -6498,7 +6502,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l if (toggled) { is_open = !is_open; - window->DC.StateStorage->SetInt(id, is_open); + window->DC.StateStorage->SetInt(storage_id, is_open); g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledOpen; } } @@ -6639,8 +6643,8 @@ bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags) ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; - - return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader, label); + ImGuiID id = window->GetID(label); + return TreeNodeBehavior(id, id, flags | ImGuiTreeNodeFlags_CollapsingHeader, label); } // p_visible == NULL : regular collapsing header @@ -6660,7 +6664,7 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFl flags |= ImGuiTreeNodeFlags_CollapsingHeader; if (p_visible) flags |= ImGuiTreeNodeFlags_AllowOverlap | (ImGuiTreeNodeFlags)ImGuiTreeNodeFlags_ClipLabelForTrailingButton; - bool is_open = TreeNodeBehavior(id, flags, label); + bool is_open = TreeNodeBehavior(id, id, flags, label); if (p_visible != NULL) { // Create a small overlapping close button From 070c046cd1fcbe694e3f19e2f5eaff9df0cb6357 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 17 Jul 2024 20:01:55 +0200 Subject: [PATCH 16/17] Internals: rename recently added TreeNodeIsOpen() -> TreeNodeGetOpen(). (#7553, #1131, #2958, #2079, #722) Amend ac7d6fb --- imgui_internal.h | 2 +- imgui_widgets.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 4523a0228..5d8f2332c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3466,7 +3466,7 @@ namespace ImGui // Widgets: Tree Nodes IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); IMGUI_API void TreePushOverrideID(ImGuiID id); - IMGUI_API bool TreeNodeIsOpen(ImGuiID storage_id); + IMGUI_API bool TreeNodeGetOpen(ImGuiID storage_id); IMGUI_API void TreeNodeSetOpen(ImGuiID storage_id, bool open); IMGUI_API bool TreeNodeUpdateNextOpen(ImGuiID storage_id, ImGuiTreeNodeFlags flags); // Return open state. Consume previous SetNextItemOpen() data, if any. May return true when logging. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 6e0b5d990..a7ab721c8 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6260,7 +6260,7 @@ bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char return TreeNodeBehavior(id, id, flags, label, label_end); } -bool ImGui::TreeNodeIsOpen(ImGuiID storage_id) +bool ImGui::TreeNodeGetOpen(ImGuiID storage_id) { ImGuiContext& g = *GImGui; ImGuiStorage* storage = g.CurrentWindow->DC.StateStorage; From c2d21ab04f26a9846fc303dae710a57446bbe4d3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 18 Jul 2024 14:02:17 +0200 Subject: [PATCH 17/17] Backends: SDL3: Update for API changes: SDL_GetClipboardText() string ownership change. (#7801) --- backends/imgui_impl_sdl3.cpp | 5 ++++- docs/CHANGELOG.txt | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index 7bb9ca217..5cd852599 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -21,6 +21,8 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-07-18: Update for SDL3 api changes: SDL_GetClipboardText() string ownership change. (#7801) +// 2024-07-15: Update for SDL3 api changes: SDL_GetProperty() change to SDL_GetPointerProperty(). (#7794) // 2024-07-02: Update for SDL3 api changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762). // 2024-07-01: Update for SDL3 api changes: SDL_SetTextInputRect() changed to SDL_SetTextInputArea(). // 2024-06-26: Update for SDL3 api changes: SDL_StartTextInput()/SDL_StopTextInput()/SDL_SetTextInputRect() functions signatures. @@ -115,7 +117,8 @@ static const char* ImGui_ImplSDL3_GetClipboardText(void*) ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); if (bd->ClipboardTextData) SDL_free(bd->ClipboardTextData); - bd->ClipboardTextData = SDL_GetClipboardText(); + const char* sdl_clipboard_text = SDL_GetClipboardText(); + bd->ClipboardTextData = sdl_clipboard_text ? SDL_strdup(sdl_clipboard_text) : NULL; return bd->ClipboardTextData; } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1bb5efa53..fea2fe5c2 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -90,6 +90,7 @@ Other changes: struct description data that a real application would want to use. - Backends: Win32: Fixed ImGuiMod_Super being mapped to VK_APPS instead of VK_LWIN||VK_RWIN. (#7768, #4858, #2622) [@Aemony] +- Backends: SDL3: Update for API changes: SDL_GetClipboardText() string ownership change. (#7801) - Backends: SDL3: Update for API changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762) - Backends: SDL3: Update for API changes: SDL_GetProperty() change to SDL_GetPointerProperty(). (#7794) [@wermipls] - Backends: SDL2,SDL3,OSX: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() rename.