From c543d93af124529efb67c567cc4991d2e72fe6c4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 19 Jul 2021 19:21:18 +0200 Subject: [PATCH] Expose BeginDisabled()/EndDisabled() in public API. Add to demo. (#211) --- docs/CHANGELOG.txt | 18 +++++++++++------- imgui.cpp | 20 ++++++++++---------- imgui.h | 9 ++++++++- imgui_demo.cpp | 32 +++++++++++++++++++++++++++++--- imgui_internal.h | 6 ++---- imgui_widgets.cpp | 16 ++++++++-------- 6 files changed, 68 insertions(+), 33 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 23e527d2c..de0b5e249 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -62,7 +62,17 @@ Breaking Changes: Other Changes: - IO: Added io.AddFocusEvent() api for backend to tell when host window has gained/lost focus. (#4388) [@thedmd] If you use a custom backend, consider adding support for this! -- Windows: ImGuiWindowFlags_UnsavedDocument/ImGuiTabItmeFlags_UnsavedDocument display a dot instead of a '*' so it +- Disabled: added BeginDisabled()/EndDisabled() api to create a scope where interactions are disabled. (#211) + - Added style.DisabledAlpha (default to 0.60f) and ImGuiStyleVar_DisabledAlpha. (#211) + - Unlike the internal-and-undocumented-but-somehow-known PushItemFlag(ImGuiItemFlags_Disabled), this also alters + visuals. Currently this is done by lowering alpha of all widgets. Future styling system may do that differently. + - Disabled items set HoveredId, allowing e.g. HoveredIdTimer to run. (#211, #3419) [@rokups] + - Disabled items more consistently release ActiveId if the active item got disabled. (#211) + - Nav: Fixed disabled items from beig candidate for default focus. (#211, #787) + - Fixed Selectable() selection not showing when disabled. (#211) + - Fixed IsItemHovered() returning true on disabled item when navigated to. (#211) + - Fixed IsItemHovered() if popped disabled state after item, or when using Selectable_Disabled. (#211) +- Windows: ImGuiWindowFlags_UnsavedDocument/ImGuiTabItmeFlags_UnsavedDocument displays a dot instead of a '*' so it is independent from font style. When in a tab, the dot is displayed at the same position as the close button. Added extra comments to clarify the purpose of this flag in the context of docked windows. - Tables: Added ImGuiTableColumnFlags_Disabled acting a master disable over (hidden from user/context menu). (#3935) @@ -81,12 +91,6 @@ Other Changes: - Menus: fix hovering a disabled menu or menu item not closing other menus. (#211) - Popups: fix BeginPopup/OpenPopup sequence failing when there are no focused windows. (#4308) [@rokups] - Nav: Alt doesn't toggle menu layer if other modifiers are held. (#4439) -- Nav: Disabled items are not candidate for default focus. (#211, #787) -- Disabled: disabled items set HoveredId, allowing e.g. HoveredIdTimer to function. (#211, #3419) [@rokups] -- Disabled: disabled mode more consistently release active id if the active item got disabled. (#211) -- Disabled: disabled mode doesn't prevent Selectable() from looking selected. (#211) -- Disabled: fixed IsItemHovered() returning true on disabled item when navigated to. (#211) -- Disabled: fixed IsItemHovered() if popped disabled state after item, or when using Selectable_Disabled. (#211) - Fixed printf-style format checks on non-MinGW flavors. (#4183, #3592) - Fonts: Functions with a 'float size_pixels' parameter can accept zero if it is set in ImFontSize::SizePixels. - Fonts: Prefer using U+FFFD character for fallback instead of '?', if available. (#4269) diff --git a/imgui.cpp b/imgui.cpp index d5d33dd47..7af9380e4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -988,7 +988,8 @@ static void* GImAllocatorUserData = NULL; ImGuiStyle::ImGuiStyle() { - Alpha = 1.0f; // Global alpha applies to everything in ImGui + Alpha = 1.0f; // Global alpha applies to everything in Dear ImGui. + DisabledAlpha = 0.60f; // Additional alpha multiplier for disabled items (multiply over current value of Alpha). WindowPadding = ImVec2(8,8); // Padding within a window WindowRounding = 0.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended. WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested. @@ -2533,6 +2534,7 @@ struct ImGuiStyleVarInfo static const ImGuiStyleVarInfo GStyleVarInfo[] = { { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, DisabledAlpha) }, // ImGuiStyleVar_DisabledAlpha { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize @@ -6615,23 +6617,21 @@ void ImGui::PopItemFlag() g.CurrentItemFlags = g.ItemFlagsStack.back(); } -// PushDisabled()/PopDisabled() -// - Those can be nested but this cannot be used to enable an already disabled section (a single PushDisabled(true) in the stack is enough to keep things disabled) -// - Those are not yet exposed in imgui.h because we are unsure of how to alter the style in a way that works for everyone. -// We may rework this. Hypothetically, a future styling system may set a flag which make widgets use different colors. +// BeginDisabled()/EndDisabled() +// - Those can be nested but this cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep things disabled) +// - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently. // - Feedback welcome at https://github.com/ocornut/imgui/issues/211 -// - You may trivially implement your own variation of this if needed. -// Here we test (CurrentItemFlags & ImGuiItemFlags_Disabled) to allow nested PushDisabled() calls. -void ImGui::PushDisabled(bool disabled) +// - BeginDisabled(false) essentially does nothing but is provided to facilitate use of boolean expressions +void ImGui::BeginDisabled(bool disabled) { ImGuiContext& g = *GImGui; bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; if (!was_disabled && disabled) - PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.6f); + PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha); PushItemFlag(ImGuiItemFlags_Disabled, was_disabled || disabled); } -void ImGui::PopDisabled() +void ImGui::EndDisabled() { ImGuiContext& g = *GImGui; bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; diff --git a/imgui.h b/imgui.h index 30e31094c..4428d96ea 100644 --- a/imgui.h +++ b/imgui.h @@ -61,7 +61,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.84 WIP" -#define IMGUI_VERSION_NUM 18314 +#define IMGUI_VERSION_NUM 18315 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -799,6 +799,11 @@ namespace ImGui IMGUI_API void EndDragDropTarget(); // only call EndDragDropTarget() if BeginDragDropTarget() returns true! IMGUI_API const ImGuiPayload* GetDragDropPayload(); // peek directly into the current payload from anywhere. may return NULL. use ImGuiPayload::IsDataType() to test for the payload type. + // Disabling [BETA API] + // - Disable all user interactions and dim items visuals (applying style.DisabledAlpha over current colors) + IMGUI_API void BeginDisabled(bool disabled = true); + IMGUI_API void EndDisabled(); + // Clipping // - Mouse hovering is affected by ImGui::PushClipRect() calls, unlike direct calls to ImDrawList::PushClipRect() which are render only. IMGUI_API void PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect); @@ -1492,6 +1497,7 @@ enum ImGuiStyleVar_ { // Enum name --------------------- // Member in ImGuiStyle structure (see ImGuiStyle for descriptions) ImGuiStyleVar_Alpha, // float Alpha + ImGuiStyleVar_DisabledAlpha, // float DisabledAlpha ImGuiStyleVar_WindowPadding, // ImVec2 WindowPadding ImGuiStyleVar_WindowRounding, // float WindowRounding ImGuiStyleVar_WindowBorderSize, // float WindowBorderSize @@ -1738,6 +1744,7 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE struct ImGuiStyle { float Alpha; // Global alpha applies to everything in Dear ImGui. + float DisabledAlpha; // Additional alpha multiplier for disabled items (multiply over current value of Alpha). ImVec2 WindowPadding; // Padding within a window. float WindowRounding; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended. float WindowBorderSize; // Thickness of border around windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 2e91eb28a..3d9045fcd 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -533,6 +533,10 @@ static void ShowDemoWindowWidgets() if (!ImGui::CollapsingHeader("Widgets")) return; + static bool disable_all = false; // The Checkbox for that is inside the "Disabled" section at the bottom + if (disable_all) + ImGui::BeginDisabled(); + if (ImGui::TreeNode("Basic")) { static int clicked = 0; @@ -2181,7 +2185,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } - if (ImGui::TreeNode("Querying Status (Edited/Active/Focused/Hovered etc.)")) + if (ImGui::TreeNode("Querying Status (Edited/Active/Hovered etc.)")) { // Select an item type const char* item_names[] = @@ -2189,16 +2193,20 @@ static void ShowDemoWindowWidgets() "Text", "Button", "Button (w/ repeat)", "Checkbox", "SliderFloat", "InputText", "InputFloat", "InputFloat3", "ColorEdit4", "Selectable", "MenuItem", "TreeNode", "TreeNode (w/ double-click)", "Combo", "ListBox" }; - static int item_type = 1; + static int item_type = 4; + static bool item_disabled = false; ImGui::Combo("Item Type", &item_type, item_names, IM_ARRAYSIZE(item_names), IM_ARRAYSIZE(item_names)); ImGui::SameLine(); HelpMarker("Testing how various types of items are interacting with the IsItemXXX functions. Note that the bool return value of most ImGui function is generally equivalent to calling ImGui::IsItemHovered()."); + ImGui::Checkbox("Item Disabled", &item_disabled); // Submit selected item item so we can query their status in the code following it. bool ret = false; static bool b = false; static float col4f[4] = { 1.0f, 0.5, 0.0f, 1.0f }; static char str[16] = {}; + if (item_disabled) + 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) @@ -2226,6 +2234,7 @@ static void ShowDemoWindowWidgets() "IsItemHovered(_AllowWhenBlockedByPopup) = %d\n" "IsItemHovered(_AllowWhenBlockedByActiveItem) = %d\n" "IsItemHovered(_AllowWhenOverlapped) = %d\n" + "IsItemHovered(_AllowWhenDisabled) = %d\n" "IsItemHovered(_RectOnly) = %d\n" "IsItemActive() = %d\n" "IsItemEdited() = %d\n" @@ -2244,6 +2253,7 @@ static void ShowDemoWindowWidgets() ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlapped), + ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled), ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly), ImGui::IsItemActive(), ImGui::IsItemEdited(), @@ -2258,6 +2268,9 @@ static void ShowDemoWindowWidgets() ImGui::GetItemRectSize().x, ImGui::GetItemRectSize().y ); + if (item_disabled) + ImGui::EndDisabled(); + static bool embed_all_inside_a_child_window = false; ImGui::Checkbox("Embed everything inside a child window (for additional testing)", &embed_all_inside_a_child_window); if (embed_all_inside_a_child_window) @@ -2327,6 +2340,18 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + + // Demonstrate BeginDisabled/EndDisabled using a checkbox located at the bottom of the section (which is a bit odd: + // logically we'd have this checkbox at the top of the section, but we don't want this feature to steal that space) + if (disable_all) + ImGui::EndDisabled(); + + if (ImGui::TreeNode("Disable block")) + { + ImGui::Checkbox("Disable entire section above", &disable_all); + ImGui::SameLine(); HelpMarker("Demonstrate using BeginDisabled()/EndDisabled() across this section."); + ImGui::TreePop(); + } } static void ShowDemoWindowLayout() @@ -2396,7 +2421,7 @@ static void ShowDemoWindowLayout() // You can also call SetNextWindowPos() to position the child window. The parent window will effectively // layout from this position. // - Using ImGui::GetItemRectMin/Max() to query the "item" state (because the child window is an item from - // the POV of the parent window). See 'Demo->Querying Status (Active/Focused/Hovered etc.)' for details. + // the POV of the parent window). See 'Demo->Querying Status (Edited/Active/Hovered etc.)' for details. { static int offset_x = 0; ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); @@ -6041,6 +6066,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) HelpMarker("When drawing circle primitives with \"num_segments == 0\" tesselation will be calculated automatically."); ImGui::DragFloat("Global Alpha", &style.Alpha, 0.005f, 0.20f, 1.0f, "%.2f"); // Not exposing zero here so user doesn't "lose" the UI (zero alpha clips all widgets). But application code could have a toggle to switch between zero and non-zero. + ImGui::DragFloat("Disabled Alpha", &style.DisabledAlpha, 0.005f, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); HelpMarker("Additional alpha multiplier for disabled items (multiply over current value of Alpha)."); ImGui::PopItemWidth(); ImGui::EndTabItem(); diff --git a/imgui_internal.h b/imgui_internal.h index a282ef1e1..4c84a78b7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -742,7 +742,7 @@ enum ImGuiItemFlags_ ImGuiItemFlags_None = 0, ImGuiItemFlags_NoTabStop = 1 << 0, // false // Disable keyboard tabbing (FIXME: should merge with _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 PushDisabled()/PushDisabled(). See github.com/ocornut/imgui/issues/211 + 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 keyboard/gamepad directional navigation (FIXME: should merge with _NoTabStop) 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 @@ -805,7 +805,7 @@ enum ImGuiButtonFlagsPrivate_ ImGuiButtonFlags_FlattenChildren = 1 << 11, // allow interactions even if a child window is overlapping ImGuiButtonFlags_AllowItemOverlap = 1 << 12, // require previous frame HoveredId to either match id or be null before being usable, use along with SetItemAllowOverlap() ImGuiButtonFlags_DontClosePopups = 1 << 13, // disable automatically closing parent popup on press // [UNUSED] - //ImGuiButtonFlags_Disabled = 1 << 14, // disable interactions -> use PushDisabled() or ImGuiItemFlags_Disabled + //ImGuiButtonFlags_Disabled = 1 << 14, // disable interactions -> use BeginDisabled() or ImGuiItemFlags_Disabled ImGuiButtonFlags_AlignTextBaseLine = 1 << 15, // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine ImGuiButtonFlags_NoKeyModifiers = 1 << 16, // disable mouse interaction if a key modifier is held ImGuiButtonFlags_NoHoldingActiveId = 1 << 17, // don't set ActiveId while holding the mouse (ImGuiButtonFlags_PressedOnClick only) @@ -2420,8 +2420,6 @@ namespace ImGui // Parameter stacks IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); IMGUI_API void PopItemFlag(); - IMGUI_API void PushDisabled(bool disabled = true); - IMGUI_API void PopDisabled(); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // If you have old/custom copy-and-pasted widgets that used FocusableItemRegister(): diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 9b84a3cdf..71ec3bbf6 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3439,7 +3439,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data style.FramePadding.x = style.FramePadding.y; ImGuiButtonFlags button_flags = ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups; if (flags & ImGuiInputTextFlags_ReadOnly) - PushDisabled(true); + BeginDisabled(true); SameLine(0, style.ItemInnerSpacing.x); if (ButtonEx("-", ImVec2(button_size, button_size), button_flags)) { @@ -3453,7 +3453,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data value_changed = true; } if (flags & ImGuiInputTextFlags_ReadOnly) - PopDisabled(); + EndDisabled(); const char* label_end = FindRenderedTextEnd(label); if (label != label_end) @@ -6163,7 +6163,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl const bool disabled_global = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; if (disabled_item && !disabled_global) // Only testing this as an optimization - PushDisabled(true); + BeginDisabled(true); // FIXME: We can standardize the behavior of those two, we could also keep the fast path of override ClipRect + full push on render only, // which would be advantageous since most selectable are not selected. @@ -6236,7 +6236,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl CloseCurrentPopup(); if (disabled_item && !disabled_global) - PopDisabled(); + EndDisabled(); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return pressed; @@ -6828,7 +6828,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) ImVec2 popup_pos, pos = window->DC.CursorPos; PushID(label); if (!enabled) - PushDisabled(); + BeginDisabled(); const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { @@ -6861,7 +6861,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) RenderArrow(window->DrawList, pos + ImVec2(offsets->OffsetMark + extra_w + g.FontSize * 0.30f, 0.0f), GetColorU32(ImGuiCol_Text), ImGuiDir_Right); } if (!enabled) - PopDisabled(); + EndDisabled(); const bool hovered = (g.HoveredId == id) && enabled; if (menuset_is_open) @@ -6994,7 +6994,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut bool pressed; PushID(label); if (!enabled) - PushDisabled(true); + BeginDisabled(true); const ImGuiSelectableFlags flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover; const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) @@ -7034,7 +7034,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut } IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0)); if (!enabled) - PopDisabled(); + EndDisabled(); PopID(); return pressed;