Skip to content

Commit 81eceb0

Browse files
committed
Internals/experimental: BeginComboPreview(), EndComboPreview(). (#4168, #1658)
1 parent 98a6292 commit 81eceb0

File tree

6 files changed

+95
-3
lines changed

6 files changed

+95
-3
lines changed

docs/TODO.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i
184184

185185
- combo: use clipper: make it easier to disable clipper with a single flag.
186186
- combo: flag for BeginCombo to not return true when unchanged (#1182)
187-
- combo: a way/helper to customize the combo preview (#1658)
187+
- combo: a way/helper to customize the combo preview (#1658) -> exeperimental BeginComboPreview()
188188
- combo/listbox: keyboard control. need InputText-like non-active focus + key handling. considering keyboard for custom listbox (pr #203)
189189
- listbox: multiple selection.
190190
- listbox: unselect option (#1208)

imgui.h

+1
Original file line numberDiff line numberDiff line change
@@ -2463,6 +2463,7 @@ struct ImDrawList
24632463
IMGUI_API void _ResetForNewFrame();
24642464
IMGUI_API void _ClearFreeMemory();
24652465
IMGUI_API void _PopUnusedDrawCmd();
2466+
IMGUI_API void _TryMergeDrawCmds();
24662467
IMGUI_API void _OnChangedClipRect();
24672468
IMGUI_API void _OnChangedTextureID();
24682469
IMGUI_API void _OnChangedVtxOffset();

imgui_demo.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -1026,8 +1026,8 @@ static void ShowDemoWindowWidgets()
10261026
// stored in the object itself, etc.)
10271027
const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" };
10281028
static int item_current_idx = 0; // Here we store our selection data as an index.
1029-
const char* combo_label = items[item_current_idx]; // Label to preview before opening the combo (technically it could be anything)
1030-
if (ImGui::BeginCombo("combo 1", combo_label, flags))
1029+
const char* combo_preview_value = items[item_current_idx]; // Pass in the preview value visible before opening the combo (it could be anything)
1030+
if (ImGui::BeginCombo("combo 1", combo_preview_value, flags))
10311031
{
10321032
for (int n = 0; n < IM_ARRAYSIZE(items); n++)
10331033
{
@@ -1043,10 +1043,12 @@ static void ShowDemoWindowWidgets()
10431043
}
10441044

10451045
// Simplified one-liner Combo() API, using values packed in a single constant string
1046+
// This is a convenience for when the selection set is small and known at compile-time.
10461047
static int item_current_2 = 0;
10471048
ImGui::Combo("combo 2 (one-liner)", &item_current_2, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0");
10481049

10491050
// Simplified one-liner Combo() using an array of const char*
1051+
// This is not very useful (may obsolete): prefer using BeginCombo()/EndCombo() for full control.
10501052
static int item_current_3 = -1; // If the selection isn't within 0..count, Combo won't display a preview
10511053
ImGui::Combo("combo 3 (array)", &item_current_3, items, IM_ARRAYSIZE(items));
10521054

imgui_draw.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,18 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data)
491491
#define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS) (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize)) // Compare ClipRect, TextureId, VtxOffset
492492
#define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC) (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize)) // Copy ClipRect, TextureId, VtxOffset
493493

494+
// Try to merge two last draw commands
495+
void ImDrawList::_TryMergeDrawCmds()
496+
{
497+
ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
498+
ImDrawCmd* prev_cmd = curr_cmd - 1;
499+
if (ImDrawCmd_HeaderCompare(curr_cmd, prev_cmd) == 0 && curr_cmd->UserCallback == NULL && prev_cmd->UserCallback == NULL)
500+
{
501+
prev_cmd->ElemCount += curr_cmd->ElemCount;
502+
CmdBuffer.pop_back();
503+
}
504+
}
505+
494506
// Our scheme may appears a bit unusual, basically we want the most-common calls AddLine AddRect etc. to not have to perform any check so we always have a command ready in the stack.
495507
// The cost of figuring out if a new command has to be added or if we can merge is paid in those Update** functions only.
496508
void ImDrawList::_OnChangedClipRect()

imgui_internal.h

+20
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,12 @@ enum ImGuiButtonFlagsPrivate_
808808
ImGuiButtonFlags_PressedOnDefault_ = ImGuiButtonFlags_PressedOnClickRelease
809809
};
810810

811+
// Extend ImGuiComboFlags_
812+
enum ImGuiComboFlagsPrivate_
813+
{
814+
ImGuiComboFlags_CustomPreview = 1 << 20 // enable BeginComboPreview()
815+
};
816+
811817
// Extend ImGuiSliderFlags_
812818
enum ImGuiSliderFlagsPrivate_
813819
{
@@ -996,6 +1002,17 @@ struct ImGuiStyleMod
9961002
ImGuiStyleMod(ImGuiStyleVar idx, ImVec2 v) { VarIdx = idx; BackupFloat[0] = v.x; BackupFloat[1] = v.y; }
9971003
};
9981004

1005+
// Storage data for BeginComboPreview()/EndComboPreview()
1006+
struct IMGUI_API ImGuiComboPreviewData
1007+
{
1008+
ImRect PreviewRect;
1009+
ImVec2 BackupCursorPos;
1010+
ImVec2 BackupCursorMaxPos;
1011+
ImGuiLayoutType BackupLayout;
1012+
1013+
ImGuiComboPreviewData() { memset(this, 0, sizeof(*this)); }
1014+
};
1015+
9991016
// Stacked storage data for BeginGroup()/EndGroup()
10001017
struct IMGUI_API ImGuiGroupData
10011018
{
@@ -1552,6 +1569,7 @@ struct ImGuiContext
15521569
float ColorEditLastSat; // Backup of last Saturation associated to LastColor[3], so we can restore Saturation in lossy RGB<>HSV round trips
15531570
float ColorEditLastColor[3];
15541571
ImVec4 ColorPickerRef; // Initial/reference color at the time of opening the color picker.
1572+
ImGuiComboPreviewData ComboPreviewData;
15551573
float SliderCurrentAccum; // Accumulated slider delta when using navigation controls.
15561574
bool SliderCurrentAccumDirty; // Has the accumulated slider delta changed since last time we tried to apply it?
15571575
bool DragCurrentAccumDirty;
@@ -2416,6 +2434,8 @@ namespace ImGui
24162434

24172435
// Combos
24182436
IMGUI_API bool BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags flags);
2437+
IMGUI_API bool BeginComboPreview();
2438+
IMGUI_API void EndComboPreview();
24192439

24202440
// Gamepad/Keyboard Navigation
24212441
IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit);

imgui_widgets.cpp

+57
Original file line numberDiff line numberDiff line change
@@ -1541,6 +1541,8 @@ void ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_exc
15411541
// - BeginCombo()
15421542
// - BeginComboPopup() [Internal]
15431543
// - EndCombo()
1544+
// - BeginComboPreview() [Internal]
1545+
// - EndComboPreview() [Internal]
15441546
// - Combo()
15451547
//-------------------------------------------------------------------------
15461548

@@ -1602,6 +1604,14 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF
16021604
}
16031605
RenderFrameBorder(bb.Min, bb.Max, style.FrameRounding);
16041606

1607+
// Custom preview
1608+
if (flags & ImGuiComboFlags_CustomPreview)
1609+
{
1610+
g.ComboPreviewData.PreviewRect = ImRect(bb.Min.x, bb.Min.y, value_x2, bb.Max.y);
1611+
IM_ASSERT(preview_value == NULL || preview_value[0] == 0);
1612+
preview_value = NULL;
1613+
}
1614+
16051615
// Render preview and label
16061616
if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview))
16071617
{
@@ -1683,6 +1693,53 @@ void ImGui::EndCombo()
16831693
EndPopup();
16841694
}
16851695

1696+
// Call directly after the BeginCombo/EndCombo block. The preview is designed to only host non-interactive elements
1697+
// (Experimental, see GitHub issues: #1658, #4168)
1698+
bool ImGui::BeginComboPreview()
1699+
{
1700+
ImGuiContext& g = *GImGui;
1701+
ImGuiWindow* window = g.CurrentWindow;
1702+
ImGuiComboPreviewData* preview_data = &g.ComboPreviewData;
1703+
1704+
if (window->SkipItems || !window->ClipRect.Overlaps(window->DC.LastItemRect)) // FIXME: Because we don't have a ImGuiItemStatusFlags_Visible flag to test last ItemAdd() result
1705+
return false;
1706+
IM_ASSERT(window->DC.LastItemRect.Min.x == preview_data->PreviewRect.Min.x && window->DC.LastItemRect.Min.y == preview_data->PreviewRect.Min.y); // Didn't call after BeginCombo/EndCombo block or forgot to pass ImGuiComboFlags_CustomPreview flag?
1707+
if (!window->ClipRect.Contains(preview_data->PreviewRect)) // Narrower test (optional)
1708+
return false;
1709+
1710+
// FIXME: This could be contained in a PushWorkRect() api
1711+
preview_data->BackupCursorPos = window->DC.CursorPos;
1712+
preview_data->BackupCursorMaxPos = window->DC.CursorMaxPos;
1713+
preview_data->BackupLayout = window->DC.LayoutType;
1714+
window->DC.CursorPos = preview_data->PreviewRect.Min + g.Style.FramePadding;
1715+
window->DC.CursorMaxPos = window->DC.CursorPos;
1716+
window->DC.LayoutType = ImGuiLayoutType_Horizontal;
1717+
PushClipRect(preview_data->PreviewRect.Min, preview_data->PreviewRect.Max, true);
1718+
1719+
return true;
1720+
}
1721+
1722+
void ImGui::EndComboPreview()
1723+
{
1724+
ImGuiContext& g = *GImGui;
1725+
ImGuiWindow* window = g.CurrentWindow;
1726+
ImGuiComboPreviewData* preview_data = &g.ComboPreviewData;
1727+
1728+
// FIXME: Using CursorMaxPos approximation instead of correct AABB which we will store in ImDrawCmd in the future
1729+
ImDrawList* draw_list = window->DrawList;
1730+
if (window->DC.CursorMaxPos.x < preview_data->PreviewRect.Max.x && window->DC.CursorMaxPos.y < preview_data->PreviewRect.Max.y)
1731+
if (draw_list->CmdBuffer.Size > 1) // Unlikely case that the PushClipRect() didn't create a command
1732+
{
1733+
draw_list->_CmdHeader.ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 2].ClipRect;
1734+
draw_list->_TryMergeDrawCmds();
1735+
}
1736+
PopClipRect();
1737+
window->DC.CursorPos = preview_data->BackupCursorPos;
1738+
window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, preview_data->BackupCursorMaxPos);
1739+
window->DC.LayoutType = preview_data->BackupLayout;
1740+
preview_data->PreviewRect = ImRect();
1741+
}
1742+
16861743
// Getter for the old Combo() API: const char*[]
16871744
static bool Items_ArrayGetter(void* data, int idx, const char** out_text)
16881745
{

0 commit comments

Comments
 (0)