MultiSelect: Added ImGuiMultiSelectFlags_SelectOnClickRelease to allow dragging an unselected item without altering selection + update drag and drop demo.

This commit is contained in:
ocornut 2023-08-31 15:50:01 +02:00
parent 5628dda5a5
commit 82de6c470b
3 changed files with 46 additions and 26 deletions

View File

@ -2736,6 +2736,8 @@ enum ImGuiMultiSelectFlags_
ImGuiMultiSelectFlags_ClearOnEscape = 1 << 2, // Clear selection when pressing Escape while scope is focused. ImGuiMultiSelectFlags_ClearOnEscape = 1 << 2, // Clear selection when pressing Escape while scope is focused.
ImGuiMultiSelectFlags_ClearOnClickWindowVoid= 1 << 3, // Clear selection when clicking on empty location within host window (use if BeginMultiSelect() covers a whole window) ImGuiMultiSelectFlags_ClearOnClickWindowVoid= 1 << 3, // Clear selection when clicking on empty location within host window (use if BeginMultiSelect() covers a whole window)
//ImGuiMultiSelectFlags_ClearOnClickRectVoid= 1 << 4, // Clear selection when clicking on empty location within rectangle covered by selection scope (use if multiple BeginMultiSelect() are used in the same host window) //ImGuiMultiSelectFlags_ClearOnClickRectVoid= 1 << 4, // Clear selection when clicking on empty location within rectangle covered by selection scope (use if multiple BeginMultiSelect() are used in the same host window)
ImGuiMultiSelectFlags_SelectOnClick = 1 << 5, // Apply selection on mouse down when clicking on unselected item. (Default)
ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 6, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection.
}; };
// Multi-selection system // Multi-selection system

View File

@ -3338,6 +3338,7 @@ static void ShowDemoWindowMultiSelect()
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectAll", &flags, ImGuiMultiSelectFlags_NoSelectAll); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectAll", &flags, ImGuiMultiSelectFlags_NoSelectAll);
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape);
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickWindowVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickWindowVoid); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickWindowVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickWindowVoid);
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClickRelease", &flags, ImGuiMultiSelectFlags_SelectOnClickRelease); ImGui::SameLine(); HelpMarker("Allow dragging an unselected item without altering selection.");
// Initialize default list with 1000 items. // Initialize default list with 1000 items.
static ImVector<int> items; static ImVector<int> items;
@ -3403,11 +3404,11 @@ static void ShowDemoWindowMultiSelect()
// IMPORTANT: for deletion refocus to work we need object ID to be stable, // IMPORTANT: for deletion refocus to work we need object ID to be stable,
// aka not depend on their index in the list. Here we use our persistent item_id // aka not depend on their index in the list. Here we use our persistent item_id
// instead of index to build a unique ID that will persist. // instead of index to build a unique ID that will persist.
// (If we used PushID(n) instead, focus wouldn't be restored correctly after deletion). // (If we used PushID(index) instead, focus wouldn't be restored correctly after deletion).
ImGui::PushID(item_id); ImGui::PushID(item_id);
// Emit a color button, to test that Shift+LeftArrow landing on an item that is not part // Emit a color button, to test that Shift+LeftArrow landing on an item that is not part
// of the selection scope doesn't erroneously alter our selection (FIXME-TESTS: Add a test for that!). // of the selection scope doesn't erroneously alter our selection.
if (show_color_button) if (show_color_button)
{ {
ImU32 dummy_col = (ImU32)((unsigned int)n * 0xC250B74B) | IM_COL32_A_MASK; ImU32 dummy_col = (ImU32)((unsigned int)n * 0xC250B74B) | IM_COL32_A_MASK;
@ -3415,38 +3416,56 @@ static void ShowDemoWindowMultiSelect()
ImGui::SameLine(); ImGui::SameLine();
} }
// Submit item
bool item_is_selected = selection.Contains((ImGuiID)n); bool item_is_selected = selection.Contains((ImGuiID)n);
bool item_is_open = false;
ImGui::SetNextItemSelectionUserData(n); ImGui::SetNextItemSelectionUserData(n);
if (widget_type == WidgetType_Selectable) if (widget_type == WidgetType_Selectable)
{ {
ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_None; ImGui::Selectable(label, item_is_selected, ImGuiSelectableFlags_None);
ImGui::Selectable(label, item_is_selected, selectable_flags);
if (item_curr_idx_to_focus == n)
ImGui::SetKeyboardFocusHere(-1);
if (use_drag_drop && ImGui::BeginDragDropSource())
{
ImGui::Text("(Dragging %d items)", selection.GetSize());
ImGui::EndDragDropSource();
}
} }
else if (widget_type == WidgetType_TreeNode) else if (widget_type == WidgetType_TreeNode)
{ {
ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth; ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
tree_node_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
if (item_is_selected) if (item_is_selected)
tree_node_flags |= ImGuiTreeNodeFlags_Selected; tree_node_flags |= ImGuiTreeNodeFlags_Selected;
bool open = ImGui::TreeNodeEx(label, tree_node_flags); item_is_open = ImGui::TreeNodeEx(label, tree_node_flags);
}
// Focus (for after deletion)
if (item_curr_idx_to_focus == n) if (item_curr_idx_to_focus == n)
ImGui::SetKeyboardFocusHere(-1); ImGui::SetKeyboardFocusHere(-1);
// Drag and Drop
if (use_drag_drop && ImGui::BeginDragDropSource()) if (use_drag_drop && ImGui::BeginDragDropSource())
{ {
ImGui::Text("(Dragging %d items)", selection.GetSize()); // Write payload with full selection OR single unselected item (only possible with ImGuiMultiSelectFlags_SelectOnClickRelease)
if (ImGui::GetDragDropPayload() == NULL)
{
ImVector<int> payload_items;
if (!item_is_selected)
payload_items.push_back(item_id);
else
for (const ImGuiStoragePair& pair : selection.Storage.Data)
if (pair.val_i)
payload_items.push_back((int)pair.key);
ImGui::SetDragDropPayload("MULTISELECT_DEMO_ITEMS", payload_items.Data, (size_t)payload_items.size_in_bytes());
}
// Display payload content in tooltip
const ImGuiPayload* payload = ImGui::GetDragDropPayload();
const int* payload_items = (int*)payload->Data;
const int payload_count = (int)payload->DataSize / (int)sizeof(payload_items[0]);
if (payload_count == 1)
ImGui::Text("Object %05d: %s", payload_items[0], ExampleNames[payload_items[0] % IM_ARRAYSIZE(ExampleNames)]);
else
ImGui::Text("Dragging %d objects", payload_count);
ImGui::EndDragDropSource(); ImGui::EndDragDropSource();
} }
if (open)
if (widget_type == WidgetType_TreeNode && item_is_open)
ImGui::TreePop(); ImGui::TreePop();
}
// Right-click: context menu // Right-click: context menu
if (ImGui::BeginPopupContextItem()) if (ImGui::BeginPopupContextItem())

View File

@ -7306,10 +7306,9 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags
// Alter button behavior flags // Alter button behavior flags
// To handle drag and drop of multiple items we need to avoid clearing selection on click. // To handle drag and drop of multiple items we need to avoid clearing selection on click.
// Enabling this test makes actions using CTRL+SHIFT delay their effect on MouseUp which is annoying, but it allows drag and drop of multiple items. // Enabling this test makes actions using CTRL+SHIFT delay their effect on MouseUp which is annoying, but it allows drag and drop of multiple items.
// FIXME-MULTISELECT: Consider opt-in for drag and drop behavior in ImGuiMultiSelectFlags?
ImGuiButtonFlags button_flags = *p_button_flags; ImGuiButtonFlags button_flags = *p_button_flags;
button_flags |= ImGuiButtonFlags_NoHoveredOnFocus; button_flags |= ImGuiButtonFlags_NoHoveredOnFocus;
if (!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore)) if ((!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore)) && !(ms->Flags & ImGuiMultiSelectFlags_SelectOnClickRelease))
button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease; button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease;
else else
button_flags |= ImGuiButtonFlags_PressedOnClickRelease; button_flags |= ImGuiButtonFlags_PressedOnClickRelease;