diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 302bb7334c9..47ea5242ba1 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -3557,6 +3557,47 @@ void Tree::_go_up() { accept_event(); } +void Tree::_shift_select_range(TreeItem *new_item) { + if (!new_item) { + new_item = selected_item; + } + int s_col = selected_col; + bool in_range = false; + TreeItem *item = root; + + if (!shift_anchor) { + shift_anchor = selected_item; + } + + while (item) { + bool at_range_edge = item == shift_anchor || item == new_item; + if (at_range_edge) { + in_range = !in_range; + } + if (new_item == shift_anchor) { + in_range = false; + } + if (item->is_visible_in_tree()) { + if (in_range || at_range_edge) { + if (!item->is_selected(selected_col) && item->is_selectable(selected_col)) { + item->select(selected_col); + emit_signal(SNAME("multi_selected"), item, selected_col, true); + } + } else if (item->is_selected(selected_col)) { + item->deselect(selected_col); + emit_signal(SNAME("multi_selected"), item, selected_col, false); + } + } + item = item->get_next_in_tree(false); + } + + selected_item = new_item; + selected_col = s_col; + ensure_cursor_is_visible(); + queue_redraw(); + accept_event(); +} + void Tree::_go_down() { TreeItem *next = nullptr; if (!selected_item) { @@ -3646,6 +3687,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(cache.rtl ? "ui_left" : "ui_right") && p_event->is_pressed()) { if (!cursor_can_exit_tree) { @@ -3687,15 +3732,25 @@ void Tree::gui_input(const Ref &p_event) { if (!cursor_can_exit_tree) { accept_event(); } - - _go_up(); + // Shift Up Selection. + if (k.is_valid() && k->is_shift_pressed() && selected_item && select_mode == SELECT_MULTI) { + TreeItem *new_item = selected_item->get_prev_visible(false); + _shift_select_range(new_item); + } else { + _go_up(); + } } else if (p_event->is_action("ui_down") && p_event->is_pressed() && !is_command) { if (!cursor_can_exit_tree) { accept_event(); } - - _go_down(); + // Shift Down Selection. + if (k.is_valid() && k->is_shift_pressed() && selected_item && select_mode == SELECT_MULTI) { + TreeItem *new_item = selected_item->get_next_visible(false); + _shift_select_range(new_item); + } else { + _go_down(); + } } else if (p_event->is_action("ui_menu") && p_event->is_pressed()) { if (allow_rmb_select && selected_item) { emit_signal(SNAME("item_mouse_selected"), get_item_rect(selected_item).position, MouseButton::RIGHT); diff --git a/scene/gui/tree.h b/scene/gui/tree.h index afcfca964c0..c630cc1c0d4 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -464,6 +464,7 @@ private: TreeItem *popup_edited_item = nullptr; TreeItem *selected_item = nullptr; TreeItem *edited_item = nullptr; + TreeItem *shift_anchor = nullptr; TreeItem *popup_pressing_edited_item = nullptr; // Candidate. int popup_pressing_edited_item_column = -1; @@ -743,6 +744,7 @@ private: void _go_right(); void _go_down(); void _go_up(); + void _shift_select_range(TreeItem *new_item); bool _scroll(bool p_horizontal, float p_pages);