diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 566d0117..d774ae4a 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3399,7 +3399,8 @@ static void ShowDemoWindowMultiSelect() ImGui::SetNextItemSelectionUserData(n); if (widget_type == WidgetType_Selectable) { - ImGui::Selectable(label, item_is_selected); + ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_None; + ImGui::Selectable(label, item_is_selected, selectable_flags); if (item_curr_idx_to_focus == n) ImGui::SetKeyboardFocusHere(-1); diff --git a/imgui_internal.h b/imgui_internal.h index 842f3411..c9889d01 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3356,7 +3356,7 @@ namespace ImGui IMGUI_API int TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data); // Multi-Select API - IMGUI_API void MultiSelectItemHeader(ImGuiID id, bool* p_selected); + IMGUI_API void MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags); IMGUI_API void MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed); // Internal Columns API (this is not exposed because we will encourage transitioning to the Tables API) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 99f64fe6..092efd84 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6356,19 +6356,11 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l // Multi-selection support (header) if (is_multi_select) { - MultiSelectItemHeader(id, &selected); - button_flags |= ImGuiButtonFlags_NoHoveredOnFocus; + // Handle multi-select + alter button flags for it + MultiSelectItemHeader(id, &selected, &button_flags); - // We absolutely need to distinguish open vs select so this is the default when multi-select is enabled. + // We absolutely need to distinguish open vs select so comes by default flags |= ImGuiTreeNodeFlags_OpenOnArrow; - - // 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? - if (!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore)) - button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease; - else - button_flags |= ImGuiButtonFlags_PressedOnClickRelease; } else { @@ -6705,15 +6697,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl const bool was_selected = selected; if (is_multi_select) { - MultiSelectItemHeader(id, &selected); - button_flags |= ImGuiButtonFlags_NoHoveredOnFocus; - - // 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 the mouse release which is annoying, but it allows drag and drop of multiple items. - if (!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore)) - button_flags |= ImGuiButtonFlags_PressedOnClick; - else - button_flags |= ImGuiButtonFlags_PressedOnClickRelease; + // Handle multi-select + alter button flags for it + MultiSelectItemHeader(id, &selected, &button_flags); } bool hovered, held; @@ -7154,52 +7139,64 @@ void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_d } } -void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) +void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags) { ImGuiContext& g = *GImGui; ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; - if (!ms->IsFocused) - return; - ImGuiMultiSelectState* storage = ms->Storage; - IM_ASSERT(g.NextItemData.FocusScopeId == g.CurrentFocusScopeId && "Forgot to call SetNextItemSelectionUserData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope"); - ImGuiSelectionUserData item_data = g.NextItemData.SelectionUserData; - - // Apply Clear/SelectAll requests requested by BeginMultiSelect(). - // This is only useful if the user hasn't processed them already, and this only works if the user isn't using the clipper. - // If you are using a clipper (aka not submitting every element of the list) you need to process the Clear/SelectAll request after calling BeginMultiSelect() bool selected = *p_selected; - if (ms->BeginIO.RequestClear) - selected = false; - else if (ms->BeginIO.RequestSelectAll) - selected = true; - - // When using SHIFT+Nav: because it can incur scrolling we cannot afford a frame of lag with the selection highlight (otherwise scrolling would happen before selection) - // For this to work, we need someone to set 'RangeSrcPassedBy = true' at some point (either clipper either SetNextItemSelectionUserData() function) - if (ms->IsSetRange) + if (ms->IsFocused) { - IM_ASSERT(id != 0 && (ms->KeyMods & ImGuiMod_Shift) != 0); - const bool is_range_dst = (ms->RangeDstPassedBy == false) && g.NavJustMovedToId == id; // Assume that g.NavJustMovedToId is not clipped. - if (is_range_dst) + ImGuiMultiSelectState* storage = ms->Storage; + ImGuiSelectionUserData item_data = g.NextItemData.SelectionUserData; + IM_ASSERT(g.NextItemData.FocusScopeId == g.CurrentFocusScopeId && "Forgot to call SetNextItemSelectionUserData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope"); + + // Apply Clear/SelectAll requests requested by BeginMultiSelect(). + // This is only useful if the user hasn't processed them already, and this only works if the user isn't using the clipper. + // If you are using a clipper (aka not submitting every element of the list) you need to process the Clear/SelectAll request after calling BeginMultiSelect() + if (ms->BeginIO.RequestClear) + selected = false; + else if (ms->BeginIO.RequestSelectAll) + selected = true; + + // When using SHIFT+Nav: because it can incur scrolling we cannot afford a frame of lag with the selection highlight (otherwise scrolling would happen before selection) + // For this to work, we need someone to set 'RangeSrcPassedBy = true' at some point (either clipper either SetNextItemSelectionUserData() function) + if (ms->IsSetRange) { - ms->RangeDstPassedBy = true; - if (storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) // If we don't have RangeSrc, assign RangeSrc = RangeDst + IM_ASSERT(id != 0 && (ms->KeyMods & ImGuiMod_Shift) != 0); + const bool is_range_dst = (ms->RangeDstPassedBy == false) && g.NavJustMovedToId == id; // Assume that g.NavJustMovedToId is not clipped. + if (is_range_dst) { - storage->RangeSrcItem = item_data; - storage->RangeSelected = selected ? 1 : 0; + ms->RangeDstPassedBy = true; + if (storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) // If we don't have RangeSrc, assign RangeSrc = RangeDst + { + storage->RangeSrcItem = item_data; + storage->RangeSelected = selected ? 1 : 0; + } } + const bool is_range_src = storage->RangeSrcItem == item_data; + if (is_range_src || is_range_dst || ms->RangeSrcPassedBy != ms->RangeDstPassedBy) + { + IM_ASSERT(storage->RangeSrcItem != ImGuiSelectionUserData_Invalid && storage->RangeSelected != -1); + selected = (storage->RangeSelected != 0); + } + else if ((ms->KeyMods & ImGuiMod_Ctrl) == 0) + selected = false; } - const bool is_range_src = storage->RangeSrcItem == item_data; - if (is_range_src || is_range_dst || ms->RangeSrcPassedBy != ms->RangeDstPassedBy) - { - IM_ASSERT(storage->RangeSrcItem != ImGuiSelectionUserData_Invalid && storage->RangeSelected != -1); - selected = (storage->RangeSelected != 0); - } - else if ((ms->KeyMods & ImGuiMod_Ctrl) == 0) - selected = false; + *p_selected = selected; } - *p_selected = selected; + // 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)) + button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease; + else + button_flags |= ImGuiButtonFlags_PressedOnClickRelease; + *p_button_flags = button_flags; } void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed)