diff --git a/imgui.h b/imgui.h index 177db867f..f7f01a4ff 100644 --- a/imgui.h +++ b/imgui.h @@ -2736,6 +2736,8 @@ enum ImGuiMultiSelectFlags_ 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_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 diff --git a/imgui_demo.cpp b/imgui_demo.cpp index ac0ddc269..6192b6343 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3338,6 +3338,7 @@ static void ShowDemoWindowMultiSelect() ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectAll", &flags, ImGuiMultiSelectFlags_NoSelectAll); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape); 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. static ImVector items; @@ -3403,11 +3404,11 @@ static void ShowDemoWindowMultiSelect() // 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 // 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); // 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) { ImU32 dummy_col = (ImU32)((unsigned int)n * 0xC250B74B) | IM_COL32_A_MASK; @@ -3415,39 +3416,57 @@ static void ShowDemoWindowMultiSelect() ImGui::SameLine(); } + // Submit item bool item_is_selected = selection.Contains((ImGuiID)n); + bool item_is_open = false; ImGui::SetNextItemSelectionUserData(n); if (widget_type == WidgetType_Selectable) { - ImGuiSelectableFlags selectable_flags = 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(); - } + ImGui::Selectable(label, item_is_selected, ImGuiSelectableFlags_None); } else if (widget_type == WidgetType_TreeNode) { - ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth; - tree_node_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; + ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; if (item_is_selected) tree_node_flags |= ImGuiTreeNodeFlags_Selected; - bool open = ImGui::TreeNodeEx(label, tree_node_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(); - } - if (open) - ImGui::TreePop(); + item_is_open = ImGui::TreeNodeEx(label, tree_node_flags); } + // Focus (for after deletion) + if (item_curr_idx_to_focus == n) + ImGui::SetKeyboardFocusHere(-1); + + // Drag and Drop + if (use_drag_drop && ImGui::BeginDragDropSource()) + { + // Write payload with full selection OR single unselected item (only possible with ImGuiMultiSelectFlags_SelectOnClickRelease) + if (ImGui::GetDragDropPayload() == NULL) + { + ImVector 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(); + } + + if (widget_type == WidgetType_TreeNode && item_is_open) + ImGui::TreePop(); + // Right-click: context menu if (ImGui::BeginPopupContextItem()) { diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 69c758dc3..102fa167a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7306,10 +7306,9 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags // Alter button behavior flags // 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. - // FIXME-MULTISELECT: Consider opt-in for drag and drop behavior in ImGuiMultiSelectFlags? ImGuiButtonFlags button_flags = *p_button_flags; 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; else button_flags |= ImGuiButtonFlags_PressedOnClickRelease;