diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index cd6c51de4b2..00570e6c209 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -3505,6 +3505,10 @@ void Tree::gui_input(const Ref &p_event) { Ref k = p_event; + if (k.is_valid() && k->get_keycode() == Key::SHIFT && !k->is_pressed()) { + shift_anchor = nullptr; + } + bool is_command = k.is_valid() && k->is_command_or_control_pressed(); if (p_event->is_action("ui_right") && p_event->is_pressed()) { if (!cursor_can_exit_tree) { @@ -3546,15 +3550,97 @@ void Tree::gui_input(const Ref &p_event) { if (!cursor_can_exit_tree) { accept_event(); } + if (k.is_valid() && k->is_shift_pressed() && selected_item && select_mode == SELECT_MULTI) { + if (!shift_anchor) { + below_shift_anchor = false; + shift_anchor = selected_item; + int sc = selected_col; - _go_up(); + // Deselect all and emit + TreeItem *item = root; + while (item) { + for (int i = 0; i < columns.size(); i++) { + if (item->is_selected(i) && !(item == selected_item && i == selected_col)) { + item->deselect(i); + emit_signal(SNAME("multi_selected"), item, i, false); + } + } + item = get_next_selected(item); + } + + // Reselect since deselect() can change selected_item + set_selected(shift_anchor, sc); + } + + TreeItem *new_item = selected_item->get_prev_visible(false); + if (new_item) { + if (new_item == shift_anchor->get_prev_visible(false)) { + below_shift_anchor = false; + } + + if (below_shift_anchor) { + selected_item->deselect(selected_col); + emit_signal(SNAME("multi_selected"), selected_item, selected_col, false); + } else { + new_item->select(selected_col); + emit_signal(SNAME("multi_selected"), new_item, selected_col, true); + } + selected_item = new_item; + ensure_cursor_is_visible(); + queue_redraw(); + } + accept_event(); + } else { + _go_up(); + } } else if (p_event->is_action("ui_down") && p_event->is_pressed() && !is_command) { if (!cursor_can_exit_tree) { accept_event(); } + if (k.is_valid() && k->is_shift_pressed() && selected_item && select_mode == SELECT_MULTI) { + if (!shift_anchor) { + below_shift_anchor = true; + shift_anchor = selected_item; + int sc = selected_col; - _go_down(); + // Deselect all and emit + TreeItem *item = root; + while (item) { + for (int i = 0; i < columns.size(); i++) { + if (item->is_selected(i) && !(item == selected_item && i == selected_col)) { + item->deselect(i); + emit_signal(SNAME("multi_selected"), item, i, false); + } + } + item = get_next_selected(item); + } + + // Reselect since deselect() can change selected_item + set_selected(shift_anchor, sc); + } + + TreeItem *new_item = selected_item->get_next_visible(false); + if (new_item) { + if (new_item == shift_anchor->get_next_visible(false)) { + below_shift_anchor = true; + } + + if (below_shift_anchor) { + new_item->select(selected_col); + emit_signal(SNAME("multi_selected"), new_item, selected_col, true); + } else { + selected_item->deselect(selected_col); + emit_signal(SNAME("multi_selected"), selected_item, selected_col, false); + } + selected_item = new_item; + ensure_cursor_is_visible(); + queue_redraw(); + } + accept_event(); + } else { + _go_down(); + } } else if (p_event->is_action("ui_page_down") && p_event->is_pressed()) { if (!cursor_can_exit_tree) { diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 9d3324bd3db..f9816831e0d 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -436,6 +436,9 @@ private: TreeItem *selected_item = nullptr; TreeItem *edited_item = nullptr; + TreeItem *shift_anchor = nullptr; + bool below_shift_anchor = true; + TreeItem *popup_pressing_edited_item = nullptr; // Candidate. int popup_pressing_edited_item_column = -1;