Mixed Value: various fixes. (#5518, #5677, #6865)

- With MixedValue+LiveEdit: a single edit that ends up with same output reports as edited.
- Standardize path sin TempInputscalar(), InputScalar(), InputTextEx().
- Refer to changes in imgui_test_suite, in particular "widgets_datatype_1", "widgets_status_liveedit_off",
This commit is contained in:
ocornut
2026-09-09 20:44:34 +02:00
parent ea6d21687b
commit e8602501bb
4 changed files with 29 additions and 7 deletions

14
imgui.h
View File

@@ -30,7 +30,7 @@
// Library Version
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
#define IMGUI_VERSION "1.93.0 WIP"
#define IMGUI_VERSION_NUM 19296
#define IMGUI_VERSION_NUM 19297
#define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000
#define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198
@@ -1258,7 +1258,6 @@ enum ImGuiItemFlags_
ImGuiItemFlags_AutoClosePopups = 1 << 4, // true // MenuItem()/Selectable() automatically close their parent popup window.
ImGuiItemFlags_AllowDuplicateId = 1 << 5, // false // Allow submitting an item with the same identifier as an item already submitted this frame without triggering a warning tooltip if io.ConfigDebugHighlightIdConflicts is set.
ImGuiItemFlags_Disabled = 1 << 6, // false // [Internal] Disable interactions. DOES NOT affect visuals. This is used by BeginDisabled()/EndDisabled() and only provided here so you can read back via GetItemFlags().
ImGuiItemFlags_MixedValue = 1 << 9, // false // [BETA] Represent a mixed/indeterminate value. Replace value label with "-" and apply edits on validation. Only supported by some widgets: Checkbox, RadioButton, Sliders and Drags.
//---------------------------------------------------------------------------------
// LiveEdit refers to applying edits to backing variables _while_ typing a value using the keyboard.
@@ -1272,6 +1271,17 @@ enum ImGuiItemFlags_
ImGuiItemFlags_LiveEditOnInputText = 1 << 7, // true // InputText: apply keyboard edits to backing value while typing. Otherwise, edits are applied when validating, tabbing out or losing focus.
ImGuiItemFlags_LiveEditOnInputScalar = 1 << 8, // false // DragXXX, SliderXXX, InputScalar: apply keyboard edits to backing value while typing. Otherwise, edits are applied when validating, tabbing out or losing focus.
ImGuiItemFlags_LiveEditOnInput = ImGuiItemFlags_LiveEditOnInputText | ImGuiItemFlags_LiveEditOnInputScalar,
//---------------------------------------------------------------------------------
// [BETA] MixedValue mode used to represent a mixed/indeterminate state, typically for multi-selection.
// - Replace value display with "-" or a custom label.
// - Enter key validation apply an edit and return true even if value hasn't changed (in order to apply to all).
// - Supported by selected widgets: Checkbox, RadioButton, Sliders, Drags, Inputs, Combo.
// - Note: InputText-side Undo cannot be reliably combined with MixedValue + LiveEdit On:
// - Both the initial edit and subsequent undo/revert will typically make your app code write to all backing objects.
// - If you use MixedMode and the simplest solution is to ensure LiveEdit is off but widgets where this applies.
//---------------------------------------------------------------------------------
ImGuiItemFlags_MixedValue = 1 << 9, // false // [BETA] Represent a mixed/indeterminate value. Replace value label with "-" and apply edits on validation.
};
// Flags for ImGui::InputText()

View File

@@ -2062,13 +2062,16 @@ static void DemoWindowWidgetsMixedValues()
// This is designed for advanced property editors which are generally reusable and data-driven.
HelpMarker("Using ImGuiItemFlags_MixedValue.");
static bool use_liveedit = false;
static float items[3] = { 12.0f, 0.0f, 0.0f };
float* item_ref = &items[0];
ImGui::Checkbox("ImGuiItemFlags_LiveEditOnInput", &use_liveedit);
ImGui::SeparatorText("Scalar/Text Widgets");
const bool is_mixed = memcmp(&items[0], &items[1], sizeof(float)) != 0 || memcmp(&items[0], &items[2], sizeof(float)) != 0;
// Demonstrate Drags, Sliders, Inputs
ImGui::PushItemFlag(ImGuiItemFlags_LiveEditOnInput, use_liveedit);
ImGui::PushItemFlag(ImGuiItemFlags_MixedValue, is_mixed);
bool edited = false;
edited |= ImGui::DragFloat("DragFloat", item_ref);
@@ -2084,6 +2087,7 @@ static void DemoWindowWidgetsMixedValues()
ImGui::InputFloat("item 0 (ref)", &items[0]);
ImGui::InputFloat("item 1", &items[1]);
ImGui::InputFloat("item 2", &items[2]);
ImGui::PopItemFlag();
// Demonstrate Checkbox(), RadioButton(), Combo(), ColorEdit4()
ImGui::SeparatorText("Others Widgets");

View File

@@ -1031,7 +1031,7 @@ enum ImGuiItemStatusFlags_
ImGuiItemStatusFlags_HasShortcut = 1 << 10, // g.LastItemData.Shortcut valid. Set by SetNextItemShortcut() -> ItemAdd().
//ImGuiItemStatusFlags_FocusedByTabbing = 1 << 8, // Removed IN 1.90.1 (Dec 2023). The trigger is part of g.NavActivateId. See commit 54c1bdeceb.
ImGuiItemStatusFlags_EditedInternal = 1 << 11, // Similar to ImGuiItemStatusFlags_Edited but bypassing ImGuiItemFlags_NoMarkEdited.
// Additional status + semantic for ImGuiTestEngine
#ifdef IMGUI_ENABLE_TEST_ENGINE
ImGuiItemStatusFlags_Openable = 1 << 20, // Item is an openable (e.g. TreeNode)
@@ -1283,6 +1283,7 @@ struct IMGUI_API ImGuiInputTextState
bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection
bool EditedBefore; // edited since activated
bool EditedThisFrame; // edited this frame
bool ValidatedThisFrame;
bool WantReloadUserBuf; // force a reload of user buf so it may be modified externally. may be automatic in future version.
ImS8 LastMoveDirectionLR; // ImGuiDir_Left or ImGuiDir_Right. track last movement direction so when cursor cross over a word-wrapping boundaries we can display it on either line depending on last move.s
int ReloadSelectionStart;

View File

@@ -3843,7 +3843,11 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG
// Only mark as edited if new value is different
g.LastItemData.ItemFlags &= ~ImGuiItemFlags_NoMarkEdited;
bool value_changed = memcmp(&data_backup, p_data, data_type_size) != 0 || (g.LastItemData.ItemFlags & ImGuiItemFlags_MixedValue);
bool value_changed = memcmp(&data_backup, p_data, data_type_size) != 0;
if (g.LastItemData.ItemFlags & ImGuiItemFlags_MixedValue)
if (ImGuiInputTextState* state = GetInputTextState(g.LastItemData.ID))
value_changed |= (g.LastItemData.ItemFlags & ImGuiItemFlags_LiveEditOnInputScalar) ? (state->EditedThisFrame | state->ValidatedThisFrame) : state->ValidatedThisFrame;
if (value_changed)
MarkItemEdited(id);
return value_changed;
@@ -3921,7 +3925,8 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data
}
}
if (g.LastItemData.ItemFlags & ImGuiItemFlags_MixedValue)
value_changed |= ret;
if (ImGuiInputTextState* state = GetInputTextState(g.LastItemData.ID))
value_changed |= (g.LastItemData.ItemFlags & ImGuiItemFlags_LiveEditOnInputScalar) ? (state->EditedThisFrame | state->ValidatedThisFrame) : state->ValidatedThisFrame;
// Step buttons
if (has_step_buttons)
@@ -5061,6 +5066,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (state != NULL && state->ID == id)
{
state->Flags = flags;
state->EditedThisFrame = state->ValidatedThisFrame = false;
//state->LastFrameActive = g.FrameCount;
// Word-wrapping: attempt to keep cursor in view while resizing frame/parent (FIXME-WORDWRAP: would be better to preserve same relative offset)
@@ -5076,7 +5082,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (g.ActiveId == id)
{
IM_ASSERT(state != NULL);
state->EditedThisFrame = false;
state->BufCapacity = buf_size;
state->WrapWidth = wrap_width;
@@ -5479,7 +5484,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (g.LastItemData.ItemFlags & ImGuiItemFlags_LiveEditOnInputText)
{
// Apply when modified
if (strcmp(state->TextSrc, buf) != 0 || (is_mixed && validated))
if (strcmp(state->TextSrc, buf) != 0 || (is_mixed && (state->EditedThisFrame || validated)))
{
apply_new_text = state->TextSrc;
apply_new_text_length = state->TextLen;
@@ -5806,6 +5811,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (label_size.x > 0)
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label, label_end, false);
if (state && validated)
state->ValidatedThisFrame = true;
if (value_changed)
MarkItemEdited(id);