1
0
mirror of https://github.com/ocornut/imgui.git synced 2024-11-28 01:20:55 +01:00

MultiSelect: BoxSelect: fixed using in frozen table. (#7821, #5143) + added Demo.

Amend 0b4a1a40
This commit is contained in:
ocornut 2024-07-29 15:23:24 +02:00
parent 249d5caedb
commit b847c41437
3 changed files with 68 additions and 6 deletions

View File

@ -3306,6 +3306,52 @@ static void ShowDemoWindowMultiSelect(DemoWindowData* demo_data)
ImGui::TreePop(); ImGui::TreePop();
} }
// Demonstrate using the clipper with BeginMultiSelect()/EndMultiSelect()
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (in a table)");
if (ImGui::TreeNode("Multi-Select (in a table)"))
{
static ImGuiSelectionBasicStorage selection;
const int ITEMS_COUNT = 10000;
ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT);
if (ImGui::BeginTable("##Basket", 2, ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter))
{
ImGui::TableSetupColumn("Object");
ImGui::TableSetupColumn("Action");
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableHeadersRow();
ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, ITEMS_COUNT);
selection.ApplyRequests(ms_io);
ImGuiListClipper clipper;
clipper.Begin(ITEMS_COUNT);
if (ms_io->RangeSrcItem != -1)
clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.
while (clipper.Step())
{
for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++)
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
char label[64];
sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]);
bool item_is_selected = selection.Contains((ImGuiID)n);
ImGui::SetNextItemSelectionUserData(n);
ImGui::Selectable(label, item_is_selected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap);
ImGui::TableNextColumn();
ImGui::SmallButton("hello");
}
}
ms_io = ImGui::EndMultiSelect();
selection.ApplyRequests(ms_io);
ImGui::EndTable();
}
ImGui::TreePop();
}
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (checkboxes)"); IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (checkboxes)");
if (ImGui::TreeNode("Multi-Select (checkboxes)")) if (ImGui::TreeNode("Multi-Select (checkboxes)"))
{ {

View File

@ -3397,7 +3397,7 @@ namespace ImGui
IMGUI_API int TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data); IMGUI_API int TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data);
// Box-Select API // Box-Select API
IMGUI_API bool BeginBoxSelect(ImGuiWindow* window, ImGuiID box_select_id, ImGuiMultiSelectFlags ms_flags); IMGUI_API bool BeginBoxSelect(const ImRect& scope_rect, ImGuiWindow* window, ImGuiID box_select_id, ImGuiMultiSelectFlags ms_flags);
IMGUI_API void EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flags); IMGUI_API void EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flags);
// Multi-Select API // Multi-Select API

View File

@ -7221,7 +7221,7 @@ static void BoxSelectScrollWithMouseDrag(ImGuiBoxSelectState* bs, ImGuiWindow* w
} }
} }
bool ImGui::BeginBoxSelect(ImGuiWindow* window, ImGuiID box_select_id, ImGuiMultiSelectFlags ms_flags) bool ImGui::BeginBoxSelect(const ImRect& scope_rect, ImGuiWindow* window, ImGuiID box_select_id, ImGuiMultiSelectFlags ms_flags)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiBoxSelectState* bs = &g.BoxSelectState; ImGuiBoxSelectState* bs = &g.BoxSelectState;
@ -7241,11 +7241,10 @@ bool ImGui::BeginBoxSelect(ImGuiWindow* window, ImGuiID box_select_id, ImGuiMult
// Current frame absolute prev/current rectangles are used to toggle selection. // Current frame absolute prev/current rectangles are used to toggle selection.
// They are derived from positions relative to scrolling space. // They are derived from positions relative to scrolling space.
const ImRect scope_rect = window->InnerClipRect;
ImVec2 start_pos_abs = WindowPosRelToAbs(window, bs->StartPosRel); ImVec2 start_pos_abs = WindowPosRelToAbs(window, bs->StartPosRel);
ImVec2 prev_end_pos_abs = WindowPosRelToAbs(window, bs->EndPosRel); // Clamped already ImVec2 prev_end_pos_abs = WindowPosRelToAbs(window, bs->EndPosRel); // Clamped already
ImVec2 curr_end_pos_abs = g.IO.MousePos; ImVec2 curr_end_pos_abs = g.IO.MousePos;
if (ms_flags & ImGuiMultiSelectFlags_ScopeWindow) // Box-select scrolling only happens with ScopeWindow if (ms_flags & ImGuiMultiSelectFlags_ScopeWindow) // Box-select scrolling only happens with ScopeWindow
curr_end_pos_abs = ImClamp(curr_end_pos_abs, scope_rect.Min, scope_rect.Max); curr_end_pos_abs = ImClamp(curr_end_pos_abs, scope_rect.Min, scope_rect.Max);
bs->BoxSelectRectPrev.Min = ImMin(start_pos_abs, prev_end_pos_abs); bs->BoxSelectRectPrev.Min = ImMin(start_pos_abs, prev_end_pos_abs);
bs->BoxSelectRectPrev.Max = ImMax(start_pos_abs, prev_end_pos_abs); bs->BoxSelectRectPrev.Max = ImMax(start_pos_abs, prev_end_pos_abs);
@ -7299,6 +7298,7 @@ void ImGui::EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flag
// [SECTION] Widgets: Multi-Select support // [SECTION] Widgets: Multi-Select support
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// - DebugLogMultiSelectRequests() [Internal] // - DebugLogMultiSelectRequests() [Internal]
// - CalcScopeRect() [Internal]
// - BeginMultiSelect() // - BeginMultiSelect()
// - EndMultiSelect() // - EndMultiSelect()
// - SetNextItemSelectionUserData() // - SetNextItemSelectionUserData()
@ -7317,6 +7317,22 @@ static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSe
} }
} }
static ImRect CalcScopeRect(ImGuiMultiSelectTempData* ms, ImGuiWindow* window)
{
if (ms->Flags & ImGuiMultiSelectFlags_ScopeRect)
{
// Warning: this depends on CursorMaxPos so it means to be called by EndMultiSelect() only
return ImRect(ms->ScopeRectMin, ImMax(window->DC.CursorMaxPos, ms->ScopeRectMin));
}
else
{
// Add inner table decoration (#7821) // FIXME: Why not baking in InnerClipRect?
ImRect scope_rect = window->InnerClipRect;
scope_rect.Min = ImMin(scope_rect.Min + ImVec2(window->DecoInnerSizeX1, window->DecoInnerSizeY1), scope_rect.Max);
return scope_rect;
}
}
// Return ImGuiMultiSelectIO structure. // Return ImGuiMultiSelectIO structure.
// Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect(). // Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect().
// Passing 'selection_size' and 'items_count' parameters is currently optional. // Passing 'selection_size' and 'items_count' parameters is currently optional.
@ -7399,7 +7415,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int sel
if (flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) if (flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d))
{ {
ms->BoxSelectId = GetID("##BoxSelect"); ms->BoxSelectId = GetID("##BoxSelect");
if (BeginBoxSelect(window, ms->BoxSelectId, flags)) if (BeginBoxSelect(CalcScopeRect(ms, window), window, ms->BoxSelectId, flags))
request_clear |= bs->RequestClear; request_clear |= bs->RequestClear;
} }
@ -7452,7 +7468,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect()
IM_ASSERT(g.CurrentMultiSelect != NULL && storage->Window == g.CurrentWindow); IM_ASSERT(g.CurrentMultiSelect != NULL && storage->Window == g.CurrentWindow);
IM_ASSERT(g.MultiSelectTempDataStacked > 0 && &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1] == g.CurrentMultiSelect); IM_ASSERT(g.MultiSelectTempDataStacked > 0 && &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1] == g.CurrentMultiSelect);
const ImRect scope_rect = (ms->Flags & ImGuiMultiSelectFlags_ScopeRect) ? ImRect(ms->ScopeRectMin, ImMax(window->DC.CursorMaxPos, ms->ScopeRectMin)) : window->InnerClipRect; ImRect scope_rect = CalcScopeRect(ms, window);
if (ms->IsFocused) if (ms->IsFocused)
{ {
// We currently don't allow user code to modify RangeSrcItem by writing to BeginIO's version, but that would be an easy change here. // We currently don't allow user code to modify RangeSrcItem by writing to BeginIO's version, but that would be an easy change here.