mirror of
https://github.com/ocornut/imgui.git
synced 2026-09-09 15:35:55 +00:00
Added ImGuiItemFlags_MixedValue support for Drags, Sliders, InputScalar, InputText, RadioButton, Combo. (#5518, #5677, #6865)
This commit is contained in:
@@ -74,6 +74,9 @@ Other Changes:
|
||||
hasn't been owned by another items. This is more correct and also necessary
|
||||
to avoid Drags/Sliders cancelling also triggering a cancel menu. (#8564, #9534)
|
||||
- Misc:
|
||||
- Added `ImGuiItemFlags_MixedValue` flag for representing mixed/indeterminate values
|
||||
in Checkbox(), RadioButton(), DragXXX(), SliderXXX(), InputXXX(), Combo() functions.
|
||||
The scoped flag is designed for usage by advanced property editors. (#5518, #5677, #6865)
|
||||
- Fixed `GetBackgroundDrawList()`/`GetForegroundDrawList()` not being properly reset
|
||||
every frame when cumulated session time is very large (e.g. a few days). [@lailoken]
|
||||
- Added `IM_NODEBUGSTEP` helper to tag functions with `__declspec(non_user_code)` (MSVC)
|
||||
@@ -90,6 +93,8 @@ Other Changes:
|
||||
- Reworked assert in PushTexture() to allow convenience grace period of using a
|
||||
texture that was queue for destroy during the frame. Make it safe to stick to
|
||||
an existing texture during the frame. (#9528, #8465)
|
||||
- Demo:
|
||||
- Added `Widgets->Mixed Values` section. (#5518, #5677, #6865)
|
||||
- Backends:
|
||||
- QNX: added QNX Screen backend. (#9492) [@mgorchak-blackberry]
|
||||
- SDL2: fixed querying framebuffer scale/density when using Metal without
|
||||
|
||||
@@ -4380,6 +4380,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
|
||||
MouseCursor = ImGuiMouseCursor_Arrow;
|
||||
MouseStationaryTimer = 0.0f;
|
||||
|
||||
MixedValueLabel = "-";
|
||||
InputTextPasswordFontBackupFlags = ImFontFlags_None;
|
||||
InputTextReactivateId = 0;
|
||||
TempInputId = 0;
|
||||
|
||||
3
imgui.h
3
imgui.h
@@ -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 19295
|
||||
#define IMGUI_VERSION_NUM 19296
|
||||
#define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000
|
||||
#define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198
|
||||
|
||||
@@ -1258,6 +1258,7 @@ 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.
|
||||
|
||||
@@ -87,6 +87,7 @@ Index of this file:
|
||||
// [SECTION] DemoWindowWidgetsImages()
|
||||
// [SECTION] DemoWindowWidgetsListBoxes()
|
||||
// [SECTION] DemoWindowWidgetsLiveEdit()
|
||||
// [SECTION] DemoWindowWidgetsMixedValues()
|
||||
// [SECTION] DemoWindowWidgetsMultiComponents()
|
||||
// [SECTION] DemoWindowWidgetsPlotting()
|
||||
// [SECTION] DemoWindowWidgetsProgressBars()
|
||||
@@ -2020,7 +2021,7 @@ static void DemoWindowWidgetsLiveEdit(ImGuiDemoWindowData* demo_data)
|
||||
{
|
||||
if (ImGui::TreeNode("Live Edit Flags"))
|
||||
{
|
||||
IMGUI_DEMO_MARKER("Widgets/Live Edit Flgs");
|
||||
IMGUI_DEMO_MARKER("Widgets/Live Edit Flags");
|
||||
|
||||
ImGui::TextWrapped("Select whether to apply keyboard edits to backing variables _while_ typing.");
|
||||
|
||||
@@ -2050,6 +2051,59 @@ static void DemoWindowWidgetsLiveEdit(ImGuiDemoWindowData* demo_data)
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] DemoWindowWidgetsMixedValues()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static void DemoWindowWidgetsMixedValues()
|
||||
{
|
||||
if (ImGui::TreeNode("Mixed Values"))
|
||||
{
|
||||
// This is designed for advanced property editors which are generally reusable and data-driven.
|
||||
HelpMarker("Using ImGuiItemFlags_MixedValue.");
|
||||
|
||||
static float items[3] = { 12.0f, 0.0f, 0.0f };
|
||||
float* item_ref = &items[0];
|
||||
|
||||
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_MixedValue, is_mixed);
|
||||
bool edited = false;
|
||||
edited |= ImGui::DragFloat("DragFloat", item_ref);
|
||||
edited |= ImGui::SliderFloat("SliderFloat", item_ref, 0.0f, 100.0f);
|
||||
edited |= ImGui::InputFloat("InputFloat", item_ref, 1.0f);
|
||||
if (edited)
|
||||
for (float& item : items)
|
||||
if (&item != item_ref)
|
||||
item = *item_ref;
|
||||
ImGui::PopItemFlag();
|
||||
|
||||
ImGui::Text("Underlying data:");
|
||||
ImGui::InputFloat("item 0 (ref)", &items[0]);
|
||||
ImGui::InputFloat("item 1", &items[1]);
|
||||
ImGui::InputFloat("item 2", &items[2]);
|
||||
|
||||
// Demonstrate Checkbox()
|
||||
// (this is automatically used by e.g. CheckboxFlags())
|
||||
ImGui::SeparatorText("Others Widgets");
|
||||
bool b_on = true, b_off = false;
|
||||
ImGui::Checkbox("Checkbox On", &b_on);
|
||||
ImGui::Checkbox("Checkbox Off", &b_off);
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_MixedValue, true);
|
||||
ImGui::Checkbox("Checkbox Mixed", &b_off);
|
||||
ImGui::RadioButton("RadioButton Mixed", true);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("RadioButton Mixed##2", true); // Showing 2 radio buttons makes the example more clear
|
||||
int combo_idx = 0;
|
||||
ImGui::Combo("Combo", &combo_idx, "One\0Two\0Three\0");
|
||||
ImGui::PopItemFlag();
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] DemoWindowWidgetsMultiComponents()
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -2230,12 +2284,14 @@ static void DemoWindowWidgetsQueryingStatuses()
|
||||
};
|
||||
static int item_type = 4;
|
||||
static bool item_disabled = false;
|
||||
static bool item_mixedvalue = false;
|
||||
static bool liveedit_flags_override = false;
|
||||
static ImGuiItemFlags liveedit_flags = 0;
|
||||
ImGui::Combo("Item Type", &item_type, item_names, IM_COUNTOF(item_names), IM_COUNTOF(item_names));
|
||||
ImGui::SameLine();
|
||||
HelpMarker("Testing how various types of items are interacting with the IsItemXXX functions. Note that the bool return value of most ImGui function is generally equivalent to calling ImGui::IsItemHovered().");
|
||||
ImGui::Checkbox("Item Disabled", &item_disabled);
|
||||
ImGui::Checkbox("Item MixedValue", &item_mixedvalue);
|
||||
ImGui::Checkbox("Override LiveEdit:", &liveedit_flags_override);
|
||||
ImGui::SameLine();
|
||||
if (!liveedit_flags_override)
|
||||
@@ -2260,6 +2316,8 @@ static void DemoWindowWidgetsQueryingStatuses()
|
||||
static char str[16] = {};
|
||||
if (item_disabled)
|
||||
ImGui::BeginDisabled(true);
|
||||
if (item_mixedvalue)
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_MixedValue, 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::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true); ret = ImGui::Button("ITEM: Button"); ImGui::PopItemFlag(); } // Testing button (with repeater)
|
||||
@@ -2343,6 +2401,8 @@ static void DemoWindowWidgetsQueryingStatuses()
|
||||
ImGui::PopItemFlag();
|
||||
ImGui::PopItemFlag();
|
||||
}
|
||||
if (item_mixedvalue)
|
||||
ImGui::PopItemFlag();
|
||||
if (item_disabled)
|
||||
ImGui::EndDisabled();
|
||||
|
||||
@@ -4505,6 +4565,7 @@ static void DemoWindowWidgets(ImGuiDemoWindowData* demo_data)
|
||||
DemoWindowWidgetsImages();
|
||||
DemoWindowWidgetsListBoxes();
|
||||
DemoWindowWidgetsLiveEdit(demo_data);
|
||||
DemoWindowWidgetsMixedValues();
|
||||
DemoWindowWidgetsMultiComponents();
|
||||
DemoWindowWidgetsPlotting();
|
||||
DemoWindowWidgetsProgressBars();
|
||||
|
||||
@@ -993,7 +993,6 @@ enum ImGuiItemFlagsPrivate_
|
||||
{
|
||||
// Controlled by user
|
||||
ImGuiItemFlags_ReadOnly = 1 << 11, // false // [ALPHA] Allow hovering interactions but underlying value is not changed.
|
||||
ImGuiItemFlags_MixedValue = 1 << 12, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets)
|
||||
ImGuiItemFlags_NoWindowHoverableCheck = 1 << 13, // false // Disable hoverable check in ItemHoverable()
|
||||
ImGuiItemFlags_AllowOverlap = 1 << 14, // false // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame.
|
||||
ImGuiItemFlags_NoNavDisableMouseHover = 1 << 15, // false // Nav keyboard/gamepad mode doesn't disable hover highlight (behave as if NavHighlightItemUnderNav==false).
|
||||
@@ -2305,6 +2304,7 @@ struct ImGuiContext
|
||||
ImVec2 WheelingAxisAvg;
|
||||
|
||||
// Item/widgets state and tracking information
|
||||
const char* MixedValueLabel; // Value replacement when displaying a mixed value. Default to "-" (Unreal uses "Multiple values", Unity uses "---"). May be interpreted as a format: must not contain single %.
|
||||
ImGuiID DebugDrawIdConflictsId; // Set when we detect multiple items with the same identifier
|
||||
ImGuiID DebugHookIdInfoId; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by ID Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line]
|
||||
ImGuiID HoveredId; // Hovered widget, filled during the frame
|
||||
|
||||
@@ -1398,7 +1398,7 @@ bool ImGui::RadioButton(const char* label, bool active)
|
||||
RenderNavCursor(total_bb, id);
|
||||
const int num_segment = window->DrawList->_CalcCircleAutoSegmentCount(radius);
|
||||
window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), num_segment);
|
||||
if (active)
|
||||
if (active && (g.LastItemData.ItemFlags & ImGuiItemFlags_MixedValue) == 0)
|
||||
{
|
||||
const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f));
|
||||
window->DrawList->AddCircleFilled(center, radius - pad, GetColorU32(ImGuiCol_CheckMark));
|
||||
@@ -2176,7 +2176,9 @@ bool ImGui::Combo(const char* label, int* current_item, const char* (*getter)(vo
|
||||
|
||||
// Call the getter to obtain the preview string which is a parameter to BeginCombo()
|
||||
const char* preview_value = NULL;
|
||||
if (*current_item >= 0 && *current_item < items_count)
|
||||
if ((g.NextItemData.ItemFlagsSet | g.CurrentItemFlags) & ImGuiItemFlags_MixedValue)
|
||||
preview_value = "";
|
||||
else if (*current_item >= 0 && *current_item < items_count)
|
||||
preview_value = getter(user_data, *current_item);
|
||||
|
||||
// The old Combo() API exposed "popup_max_height_in_items". The new more general BeginCombo() API doesn't have/need it, but we emulate it here.
|
||||
@@ -2815,8 +2817,9 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
|
||||
MarkItemEdited(id);
|
||||
|
||||
// Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
|
||||
const char* format_for_display = (g.LastItemData.ItemFlags & ImGuiItemFlags_MixedValue) ? g.MixedValueLabel : format;
|
||||
char value_buf[64];
|
||||
const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_COUNTOF(value_buf), data_type, p_data, format);
|
||||
const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_COUNTOF(value_buf), data_type, p_data, format_for_display);
|
||||
if (g.LogEnabled)
|
||||
LogSetNextTextDecoration("{", "}");
|
||||
RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f));
|
||||
@@ -3422,8 +3425,9 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
|
||||
window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);
|
||||
|
||||
// Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
|
||||
const char* format_for_display = (g.LastItemData.ItemFlags & ImGuiItemFlags_MixedValue) ? g.MixedValueLabel : format;
|
||||
char value_buf[64];
|
||||
const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_COUNTOF(value_buf), data_type, p_data, format);
|
||||
const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_COUNTOF(value_buf), data_type, p_data, format_for_display);
|
||||
if (g.LogEnabled)
|
||||
LogSetNextTextDecoration("{", "}");
|
||||
RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f));
|
||||
@@ -3576,8 +3580,9 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d
|
||||
|
||||
// Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
|
||||
// For the vertical slider we allow centered text to overlap the frame padding
|
||||
const char* format_for_display = (g.LastItemData.ItemFlags & ImGuiItemFlags_MixedValue) ? g.MixedValueLabel : format;
|
||||
char value_buf[64];
|
||||
const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_COUNTOF(value_buf), data_type, p_data, format);
|
||||
const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_COUNTOF(value_buf), data_type, p_data, format_for_display);
|
||||
RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.0f));
|
||||
if (label_size.x > 0.0f)
|
||||
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label, label_end, false);
|
||||
@@ -3815,7 +3820,7 @@ 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;
|
||||
bool value_changed = memcmp(&data_backup, p_data, data_type_size) != 0 || (g.LastItemData.ItemFlags & ImGuiItemFlags_MixedValue);
|
||||
if (value_changed)
|
||||
MarkItemEdited(id);
|
||||
return value_changed;
|
||||
@@ -3892,6 +3897,8 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data
|
||||
value_changed = DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL);
|
||||
}
|
||||
}
|
||||
if (g.LastItemData.ItemFlags & ImGuiItemFlags_MixedValue)
|
||||
value_changed |= ret;
|
||||
|
||||
// Step buttons
|
||||
if (has_step_buttons)
|
||||
@@ -4857,6 +4864,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;
|
||||
const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0;
|
||||
const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0;
|
||||
const bool is_mixed = (g.LastItemData.ItemFlags & ImGuiItemFlags_MixedValue) != 0;
|
||||
if (is_resizable)
|
||||
IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag!
|
||||
|
||||
@@ -5021,7 +5029,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
|
||||
// Select the buffer to render.
|
||||
const bool buf_display_from_state = (render_cursor || render_selection || g.ActiveId == id) && !is_readonly && state;
|
||||
bool is_displaying_hint = (hint != NULL && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0);
|
||||
bool is_displaying_hint = (hint != NULL && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0) && !is_mixed;
|
||||
|
||||
// Password pushes a temporary font with only a fallback glyph
|
||||
if (is_password && !is_displaying_hint)
|
||||
@@ -5448,7 +5456,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)
|
||||
if (strcmp(state->TextSrc, buf) != 0 || (is_mixed && validated))
|
||||
{
|
||||
apply_new_text = state->TextSrc;
|
||||
apply_new_text_length = state->TextLen;
|
||||
@@ -5458,7 +5466,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
else
|
||||
{
|
||||
// Apply on validation/deactivation, otherwise cancel out previous apply attempts (e.g. revert)
|
||||
value_changed = ((validated || clear_active_id || revert_edit) && strcmp(state->TextSrc, buf) != 0);
|
||||
value_changed = (validated || clear_active_id || revert_edit) && (strcmp(state->TextSrc, buf) != 0 || (is_mixed && validated));
|
||||
apply_new_text = value_changed ? state->TextSrc : NULL;
|
||||
apply_new_text_length = value_changed ? state->TextLen : 0;
|
||||
}
|
||||
@@ -5543,7 +5551,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
|
||||
// Display hint when contents is empty
|
||||
// At this point we need to handle the possibility that a callback could have modified the underlying buffer (#8368)
|
||||
const bool new_is_displaying_hint = (hint != NULL && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0);
|
||||
const bool new_is_displaying_hint = (hint != NULL && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0) && !is_mixed;
|
||||
if (new_is_displaying_hint != is_displaying_hint)
|
||||
{
|
||||
if (is_password && !is_displaying_hint)
|
||||
@@ -5552,10 +5560,16 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
if (is_password && !is_displaying_hint)
|
||||
PushPasswordFont();
|
||||
}
|
||||
if (is_displaying_hint)
|
||||
if (is_mixed && g.ActiveId != id && apply_new_text == NULL)
|
||||
{
|
||||
buf_display = g.MixedValueLabel;
|
||||
buf_display_end = buf_display + strlen(g.MixedValueLabel);
|
||||
render_cursor = render_selection = false;
|
||||
}
|
||||
else if (is_displaying_hint)
|
||||
{
|
||||
buf_display = hint;
|
||||
buf_display_end = hint + ImStrlen(hint);
|
||||
buf_display_end = buf_display + ImStrlen(buf_display);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user