RangeSelect/MultiSelect: Fix for TreeNode following merge of 011d4755. Demo: basic test for tree nodes.

This commit is contained in:
ocornut 2020-03-11 21:57:18 +01:00
parent dd52a2854c
commit 304270a99a
2 changed files with 50 additions and 18 deletions

View File

@ -1259,18 +1259,29 @@ static void ShowDemoWindowWidgets()
"Cauliflower", "Celery", "Celery Root", "Celcuce", "Chayote", "Celtuce", "Chayote", "Chinese Broccoli", "Corn", "Cucumber" "Cauliflower", "Celery", "Celery Root", "Celcuce", "Chayote", "Celtuce", "Chayote", "Chinese Broccoli", "Corn", "Cucumber"
}; };
int COUNT = 1000; // Test both Selectable() and TreeNode() widgets
HelpMarker("Hold CTRL and click to select multiple items. Hold SHIFT to select a range. Keyboard is also supported."); enum WidgetType { WidgetType_Selectable, WidgetType_TreeNode };
ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", (unsigned int *)&ImGui::GetIO().ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard); static WidgetType widget_type = WidgetType_TreeNode;
if (ImGui::RadioButton("Selectables", widget_type == WidgetType_Selectable)) { widget_type = WidgetType_Selectable; }
ImGui::SameLine();
if (ImGui::RadioButton("Tree nodes", widget_type == WidgetType_TreeNode)) { widget_type = WidgetType_TreeNode; }
ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", (unsigned int*)&ImGui::GetIO().ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard);
ImGui::SameLine(); HelpMarker("Hold CTRL and click to select multiple items. Hold SHIFT to select a range. Keyboard is also supported.");
// Open a scrolling region
const int ITEMS_COUNT = 1000;
if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20)))
{ {
ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_None, (void*)(intptr_t)selection_ref, selection.GetSelected((int)selection_ref));
if (multi_select_data->RequestClear) { selection.Clear(); }
if (multi_select_data->RequestSelectAll) { selection.SelectAll(COUNT); }
ImVec2 color_button_sz(ImGui::GetFontSize(), ImGui::GetFontSize()); ImVec2 color_button_sz(ImGui::GetFontSize(), ImGui::GetFontSize());
if (widget_type == WidgetType_TreeNode)
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f));
ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_None, (void*)(intptr_t)selection_ref, selection.GetSelected(selection_ref));
if (multi_select_data->RequestClear) { selection.Clear(); }
if (multi_select_data->RequestSelectAll) { selection.SelectAll(ITEMS_COUNT); }
ImGuiListClipper clipper; ImGuiListClipper clipper;
clipper.Begin(COUNT); clipper.Begin(ITEMS_COUNT);
while (clipper.Step()) while (clipper.Step())
{ {
if (clipper.DisplayStart > (int)selection_ref) if (clipper.DisplayStart > (int)selection_ref)
@ -1283,23 +1294,42 @@ static void ShowDemoWindowWidgets()
bool item_is_selected = selection.GetSelected(n); bool item_is_selected = selection.GetSelected(n);
// 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. // of the selection scope doesn't erroneously alter our selection (FIXME-TESTS: Add a test for that!).
ImVec4 dummy_col = ImColor((ImU32)ImGui::GetID(label)); ImU32 dummy_col = (ImU32)ImGui::GetID(label);
ImGui::ColorButton("##", dummy_col, ImGuiColorEditFlags_NoTooltip, color_button_sz); ImGui::ColorButton("##", ImColor(dummy_col), ImGuiColorEditFlags_NoTooltip, color_button_sz);
ImGui::SameLine(); ImGui::SameLine();
ImGui::SetNextItemSelectionData((void*)(intptr_t)n); ImGui::SetNextItemSelectionData((void*)(intptr_t)n);
if (widget_type == WidgetType_Selectable)
{
if (ImGui::Selectable(label, item_is_selected)) if (ImGui::Selectable(label, item_is_selected))
selection.SetSelected(n, !item_is_selected); selection.SetSelected(n, !item_is_selected);
}
else if (widget_type == WidgetType_TreeNode)
{
ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_OpenOnDoubleClick;
if (item_is_selected)
tree_node_flags |= ImGuiTreeNodeFlags_Selected;
ImGui::TreeNodeEx(label, tree_node_flags);
if (ImGui::IsItemToggledSelection())
selection.SetSelected(n, !item_is_selected);
}
ImGui::PopID(); ImGui::PopID();
} }
} }
// Apply multi-select requests
multi_select_data = ImGui::EndMultiSelect(); multi_select_data = ImGui::EndMultiSelect();
selection_ref = (int)(intptr_t)multi_select_data->RangeSrc; selection_ref = (int)(intptr_t)multi_select_data->RangeSrc;
ImGui::EndListBox();
if (multi_select_data->RequestClear) { selection.Clear(); } if (multi_select_data->RequestClear) { selection.Clear(); }
if (multi_select_data->RequestSelectAll) { selection.SelectAll(COUNT); } if (multi_select_data->RequestSelectAll) { selection.SelectAll(ITEMS_COUNT); }
if (multi_select_data->RequestSetRange) { selection.SetRange((int)(intptr_t)multi_select_data->RangeSrc, (int)(intptr_t)multi_select_data->RangeDst, multi_select_data->RangeValue ? 1 : 0); } if (multi_select_data->RequestSetRange) { selection.SetRange((int)(intptr_t)multi_select_data->RangeSrc, (int)(intptr_t)multi_select_data->RangeDst, multi_select_data->RangeValue ? 1 : 0); }
if (widget_type == WidgetType_TreeNode)
ImGui::PopStyleVar();
ImGui::EndListBox();
} }
ImGui::TreePop(); ImGui::TreePop();
} }

View File

@ -5901,8 +5901,6 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
const float arrow_hit_x1 = (text_pos.x - text_offset_x) - style.TouchExtraPadding.x; const float arrow_hit_x1 = (text_pos.x - text_offset_x) - style.TouchExtraPadding.x;
const float arrow_hit_x2 = (text_pos.x - text_offset_x) + (g.FontSize + padding.x * 2.0f) + style.TouchExtraPadding.x; const float arrow_hit_x2 = (text_pos.x - text_offset_x) + (g.FontSize + padding.x * 2.0f) + style.TouchExtraPadding.x;
const bool is_mouse_x_over_arrow = (g.IO.MousePos.x >= arrow_hit_x1 && g.IO.MousePos.x < arrow_hit_x2); const bool is_mouse_x_over_arrow = (g.IO.MousePos.x >= arrow_hit_x1 && g.IO.MousePos.x < arrow_hit_x2);
if (window != g.HoveredWindow || !is_mouse_x_over_arrow)
button_flags |= ImGuiButtonFlags_NoKeyModifiers;
// Open behaviors can be altered with the _OpenOnArrow and _OnOnDoubleClick flags. // Open behaviors can be altered with the _OpenOnArrow and _OnOnDoubleClick flags.
// Some alteration have subtle effects (e.g. toggle on MouseUp vs MouseDown events) due to requirements for multi-selection and drag and drop support. // Some alteration have subtle effects (e.g. toggle on MouseUp vs MouseDown events) due to requirements for multi-selection and drag and drop support.
@ -5927,12 +5925,15 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
const bool is_multi_select = g.MultiSelectEnabled; const bool is_multi_select = g.MultiSelectEnabled;
if (is_multi_select) if (is_multi_select)
{ {
flags |= ImGuiTreeNodeFlags_OpenOnArrow;
MultiSelectItemHeader(id, &selected); MultiSelectItemHeader(id, &selected);
button_flags |= ImGuiButtonFlags_NoHoveredOnFocus; button_flags |= ImGuiButtonFlags_NoHoveredOnFocus;
// We absolutely need to distinguish open vs select so this is the default when multi-select is enabled.
flags |= ImGuiTreeNodeFlags_OpenOnArrow;
// 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 the mouse release which is annoying, but it allows drag and drop of multiple items. // 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.
// FIXME-MULTISELECT: Consider opt-in for drag and drop behavior in ImGuiMultiSelectFlags?
if (!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore)) if (!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore))
button_flags |= ImGuiButtonFlags_PressedOnClick; button_flags |= ImGuiButtonFlags_PressedOnClick;
else else
@ -5940,6 +5941,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
} }
else else
{ {
if (window != g.HoveredWindow || !is_mouse_x_over_arrow)
button_flags |= ImGuiButtonFlags_NoKeyModifiers; button_flags |= ImGuiButtonFlags_NoKeyModifiers;
} }