diff options
-rw-r--r-- | doc/classes/TabBar.xml | 6 | ||||
-rw-r--r-- | doc/classes/TabContainer.xml | 6 | ||||
-rw-r--r-- | editor/editor_themes.cpp | 15 | ||||
-rw-r--r-- | editor/icons/GuiTabDropMark.svg | 1 | ||||
-rw-r--r-- | scene/gui/tab_bar.cpp | 100 | ||||
-rw-r--r-- | scene/gui/tab_bar.h | 1 | ||||
-rw-r--r-- | scene/gui/tab_container.cpp | 49 | ||||
-rw-r--r-- | scene/resources/default_theme/default_theme.cpp | 4 | ||||
-rw-r--r-- | scene/resources/default_theme/tabs_drop_mark.svg | 1 |
9 files changed, 158 insertions, 25 deletions
diff --git a/doc/classes/TabBar.xml b/doc/classes/TabBar.xml index 420ab2adeb..aca5e2d7f3 100644 --- a/doc/classes/TabBar.xml +++ b/doc/classes/TabBar.xml @@ -323,6 +323,9 @@ </constant> </constants> <theme_items> + <theme_item name="drop_mark_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)"> + Modulation color for the [theme_item drop_mark] icon. + </theme_item> <theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.5)"> Font color of disabled tabs. </theme_item> @@ -356,6 +359,9 @@ <theme_item name="decrement_highlight" data_type="icon" type="Texture2D"> Icon for the left arrow button that appears when there are too many tabs to fit in the container width. Used when the button is being hovered with the cursor. </theme_item> + <theme_item name="drop_mark" data_type="icon" type="Texture2D"> + Icon shown to indicate where a dragged tab is gonna be dropped (see [member drag_to_rearrange_enabled]). + </theme_item> <theme_item name="increment" data_type="icon" type="Texture2D"> Icon for the right arrow button that appears when there are too many tabs to fit in the container width. When the button is disabled (i.e. the last tab is visible) it appears semi-transparent. </theme_item> diff --git a/doc/classes/TabContainer.xml b/doc/classes/TabContainer.xml index ec2be012e9..c506152546 100644 --- a/doc/classes/TabContainer.xml +++ b/doc/classes/TabContainer.xml @@ -172,6 +172,9 @@ </signal> </signals> <theme_items> + <theme_item name="drop_mark_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)"> + Modulation color for the [theme_item drop_mark] icon. + </theme_item> <theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.5)"> Font color of disabled tabs. </theme_item> @@ -206,6 +209,9 @@ <theme_item name="decrement_highlight" data_type="icon" type="Texture2D"> Icon for the left arrow button that appears when there are too many tabs to fit in the container width. Used when the button is being hovered with the cursor. </theme_item> + <theme_item name="drop_mark" data_type="icon" type="Texture2D"> + Icon shown to indicate where a dragged tab is gonna be dropped (see [member drag_to_rearrange_enabled]). + </theme_item> <theme_item name="increment" data_type="icon" type="Texture2D"> Icon for the right arrow button that appears when there are too many tabs to fit in the container width. When the button is disabled (i.e. the last tab is visible) it appears semi-transparent. </theme_item> diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 4bd4e073d7..1fea759a90 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -635,7 +635,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { style_tab_selected->set_border_width_all(0); style_tab_selected->set_border_width(SIDE_TOP, Math::round(2 * EDSCALE)); // Make the highlight line prominent, but not too prominent as to not be distracting. - style_tab_selected->set_border_color(dark_color_2.lerp(accent_color, 0.75)); + Color tab_highlight = dark_color_2.lerp(accent_color, 0.75); + style_tab_selected->set_border_color(tab_highlight); // Don't round the top corners to avoid creating a small blank space between the tabs and the main panel. // This also makes the top highlight look better. style_tab_selected->set_corner_radius_all(0); @@ -1079,17 +1080,19 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_stylebox("tab_selected", "TabBar", style_tab_selected); theme->set_stylebox("tab_unselected", "TabBar", style_tab_unselected); theme->set_stylebox("tab_disabled", "TabBar", style_tab_disabled); + theme->set_stylebox("button_pressed", "TabBar", style_menu); + theme->set_stylebox("button_highlight", "TabBar", style_menu); + theme->set_stylebox("SceneTabFG", "EditorStyles", style_tab_selected); + theme->set_stylebox("SceneTabBG", "EditorStyles", style_tab_unselected); theme->set_color("font_selected_color", "TabContainer", font_color); theme->set_color("font_unselected_color", "TabContainer", font_disabled_color); theme->set_color("font_selected_color", "TabBar", font_color); theme->set_color("font_unselected_color", "TabBar", font_disabled_color); + theme->set_color("drop_mark_color", "TabContainer", tab_highlight); + theme->set_color("drop_mark_color", "TabBar", tab_highlight); theme->set_icon("menu", "TabContainer", theme->get_icon(SNAME("GuiTabMenu"), SNAME("EditorIcons"))); theme->set_icon("menu_highlight", "TabContainer", theme->get_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons"))); - theme->set_stylebox("SceneTabFG", "EditorStyles", style_tab_selected); - theme->set_stylebox("SceneTabBG", "EditorStyles", style_tab_unselected); theme->set_icon("close", "TabBar", theme->get_icon(SNAME("GuiClose"), SNAME("EditorIcons"))); - theme->set_stylebox("button_pressed", "TabBar", style_menu); - theme->set_stylebox("button_highlight", "TabBar", style_menu); theme->set_icon("increment", "TabContainer", theme->get_icon(SNAME("GuiScrollArrowRight"), SNAME("EditorIcons"))); theme->set_icon("decrement", "TabContainer", theme->get_icon(SNAME("GuiScrollArrowLeft"), SNAME("EditorIcons"))); theme->set_icon("increment", "TabBar", theme->get_icon(SNAME("GuiScrollArrowRight"), SNAME("EditorIcons"))); @@ -1098,6 +1101,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_icon("decrement_highlight", "TabBar", theme->get_icon(SNAME("GuiScrollArrowLeftHl"), SNAME("EditorIcons"))); theme->set_icon("increment_highlight", "TabContainer", theme->get_icon(SNAME("GuiScrollArrowRightHl"), SNAME("EditorIcons"))); theme->set_icon("decrement_highlight", "TabContainer", theme->get_icon(SNAME("GuiScrollArrowLeftHl"), SNAME("EditorIcons"))); + theme->set_icon("drop_mark", "TabContainer", theme->get_icon(SNAME("GuiTabDropMark"), SNAME("EditorIcons"))); + theme->set_icon("drop_mark", "TabBar", theme->get_icon(SNAME("GuiTabDropMark"), SNAME("EditorIcons"))); theme->set_constant("hseparation", "TabBar", 4 * EDSCALE); // Content of each tab diff --git a/editor/icons/GuiTabDropMark.svg b/editor/icons/GuiTabDropMark.svg new file mode 100644 index 0000000000..c85b165c71 --- /dev/null +++ b/editor/icons/GuiTabDropMark.svg @@ -0,0 +1 @@ +<svg height="32" viewBox="0 0 16 32" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m5 2h6v30h-6z" fill="#fff"/></svg> diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp index 8128bbd11d..ce2dca0ea3 100644 --- a/scene/gui/tab_bar.cpp +++ b/scene/gui/tab_bar.cpp @@ -35,6 +35,7 @@ #include "scene/gui/box_container.h" #include "scene/gui/label.h" #include "scene/gui/texture_rect.h" +#include "scene/main/viewport.h" Size2 TabBar::get_minimum_size() const { Size2 ms; @@ -158,7 +159,13 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) { } } + if (get_viewport()->gui_is_dragging() && can_drop_data(pos, get_viewport()->gui_get_drag_data())) { + dragging_valid_tab = true; + update(); + } + _update_hover(); + return; } @@ -333,6 +340,13 @@ void TabBar::_notification(int p_what) { } } break; + case NOTIFICATION_DRAG_END: { + if (dragging_valid_tab) { + dragging_valid_tab = false; + update(); + } + } break; + case NOTIFICATION_DRAW: { if (tabs.is_empty()) { return; @@ -346,8 +360,6 @@ void TabBar::_notification(int p_what) { Color font_disabled_color = get_theme_color(SNAME("font_disabled_color")); Ref<Texture2D> incr = get_theme_icon(SNAME("increment")); Ref<Texture2D> decr = get_theme_icon(SNAME("decrement")); - Ref<Texture2D> incr_hl = get_theme_icon(SNAME("increment_highlight")); - Ref<Texture2D> decr_hl = get_theme_icon(SNAME("decrement_highlight")); bool rtl = is_layout_rtl(); Vector2 size = get_size(); @@ -391,7 +403,10 @@ void TabBar::_notification(int p_what) { } if (buttons_visible) { - int vofs = (get_size().height - incr->get_size().height) / 2; + Ref<Texture2D> incr_hl = get_theme_icon(SNAME("increment_highlight")); + Ref<Texture2D> decr_hl = get_theme_icon(SNAME("decrement_highlight")); + + int vofs = (size.height - incr->get_size().height) / 2; if (rtl) { if (missing_right) { @@ -419,6 +434,39 @@ void TabBar::_notification(int p_what) { } } } + + if (dragging_valid_tab) { + int x; + + int tab_hover = get_hovered_tab(); + if (tab_hover != -1) { + Rect2 tab_rect = get_tab_rect(tab_hover); + + x = tab_rect.position.x; + if (get_local_mouse_position().x > x + tab_rect.size.width / 2) { + x += tab_rect.size.width; + } + } else { + if (rtl ^ (get_local_mouse_position().x < get_tab_rect(0).position.x)) { + x = get_tab_rect(0).position.x; + if (rtl) { + x += get_tab_rect(0).size.width; + } + } else { + Rect2 tab_rect = get_tab_rect(get_tab_count() - 1); + + x = tab_rect.position.x; + if (!rtl) { + x += tab_rect.size.width; + } + } + } + + Ref<Texture2D> drop_mark = get_theme_icon(SNAME("drop_mark")); + Color drop_mark_color = get_theme_color(SNAME("drop_mark_color")); + + drop_mark->draw(get_canvas_item(), Point2(x - drop_mark->get_width() / 2, (size.height - drop_mark->get_height()) / 2), drop_mark_color); + } } break; } } @@ -906,6 +954,8 @@ void TabBar::_on_mouse_exited() { cb_hover = -1; hover = -1; highlight_arrow = -1; + dragging_valid_tab = false; + update(); } @@ -1057,13 +1107,29 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) { NodePath to_path = get_path(); if (from_path == to_path) { - if (hover_now < 0) { - hover_now = get_tab_count() - 1; + if (tab_from_id == hover_now) { + return; + } + + // Drop the new tab to the left or right depending on where the target tab is being hovered. + if (hover_now != -1) { + Rect2 tab_rect = get_tab_rect(hover_now); + if (is_layout_rtl() ^ (p_point.x <= tab_rect.position.x + tab_rect.size.width / 2)) { + if (hover_now > tab_from_id) { + hover_now -= 1; + } + } else if (tab_from_id > hover_now) { + hover_now += 1; + } + } else { + hover_now = is_layout_rtl() ^ (p_point.x < get_tab_rect(0).position.x) ? 0 : get_tab_count() - 1; } move_tab(tab_from_id, hover_now); - emit_signal(SNAME("active_tab_rearranged"), hover_now); - set_current_tab(hover_now); + if (!is_tab_disabled(hover_now)) { + emit_signal(SNAME("active_tab_rearranged"), hover_now); + set_current_tab(hover_now); + } } else if (get_tabs_rearrange_group() != -1) { // Drag and drop between Tabs. @@ -1075,11 +1141,17 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) { return; } - Tab moving_tab = from_tabs->tabs[tab_from_id]; - if (hover_now < 0) { - hover_now = get_tab_count(); + // Drop the new tab to the left or right depending on where the target tab is being hovered. + if (hover_now != -1) { + Rect2 tab_rect = get_tab_rect(hover_now); + if (is_layout_rtl() ^ (p_point.x > tab_rect.position.x + tab_rect.size.width / 2)) { + hover_now += 1; + } + } else { + hover_now = is_layout_rtl() ^ (p_point.x < get_tab_rect(0).position.x) ? 0 : get_tab_count(); } + Tab moving_tab = from_tabs->tabs[tab_from_id]; from_tabs->remove_tab(tab_from_id); tabs.insert(hover_now, moving_tab); @@ -1092,7 +1164,13 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) { } } - set_current_tab(hover_now); + if (!is_tab_disabled(hover_now)) { + set_current_tab(hover_now); + } else { + _update_cache(); + update(); + } + update_minimum_size(); if (tabs.size() == 1) { diff --git a/scene/gui/tab_bar.h b/scene/gui/tab_bar.h index e0c4ba85ef..548a2e62af 100644 --- a/scene/gui/tab_bar.h +++ b/scene/gui/tab_bar.h @@ -101,6 +101,7 @@ private: int max_width = 0; bool scrolling_enabled = true; bool drag_to_rearrange_enabled = false; + bool dragging_valid_tab = false; bool scroll_to_selected = true; int tabs_rearrange_group = -1; diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index ee61c862b7..1697e743be 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -173,9 +173,9 @@ void TabContainer::_notification(int p_what) { int x = is_layout_rtl() ? 0 : get_size().width - menu->get_width(); if (menu_hovered) { - menu_hl->draw(get_canvas_item(), Size2(x, (header_height - menu_hl->get_height()) / 2)); + menu_hl->draw(get_canvas_item(), Point2(x, (header_height - menu_hl->get_height()) / 2)); } else { - menu->draw(get_canvas_item(), Size2(x, (header_height - menu->get_height()) / 2)); + menu->draw(get_canvas_item(), Point2(x, (header_height - menu->get_height()) / 2)); } } } break; @@ -201,6 +201,8 @@ void TabContainer::_on_theme_changed() { tab_bar->add_theme_icon_override(SNAME("increment_highlight"), get_theme_icon(SNAME("increment_highlight"))); tab_bar->add_theme_icon_override(SNAME("decrement"), get_theme_icon(SNAME("decrement"))); tab_bar->add_theme_icon_override(SNAME("decrement_highlight"), get_theme_icon(SNAME("decrement_highlight"))); + tab_bar->add_theme_icon_override(SNAME("drop_mark"), get_theme_icon(SNAME("drop_mark"))); + tab_bar->add_theme_color_override(SNAME("drop_mark_color"), get_theme_color(SNAME("drop_mark_color"))); tab_bar->add_theme_color_override(SNAME("font_selected_color"), get_theme_color(SNAME("font_selected_color"))); tab_bar->add_theme_color_override(SNAME("font_unselected_color"), get_theme_color(SNAME("font_unselected_color"))); tab_bar->add_theme_color_override(SNAME("font_disabled_color"), get_theme_color(SNAME("font_disabled_color"))); @@ -384,8 +386,6 @@ void TabContainer::_drop_data_fw(const Point2 &p_point, const Variant &p_data, C return; } - int hover_now = get_tab_idx_at_point(p_point); - Dictionary d = p_data; if (!d.has("type")) { return; @@ -393,11 +393,27 @@ void TabContainer::_drop_data_fw(const Point2 &p_point, const Variant &p_data, C if (String(d["type"]) == "tabc_element") { int tab_from_id = d["tabc_element"]; + int hover_now = get_tab_idx_at_point(p_point); NodePath from_path = d["from_path"]; NodePath to_path = get_path(); + if (from_path == to_path) { - if (hover_now < 0) { - hover_now = get_tab_count() - 1; + if (tab_from_id == hover_now) { + return; + } + + // Drop the new tab to the left or right depending on where the target tab is being hovered. + if (hover_now != -1) { + Rect2 tab_rect = tab_bar->get_tab_rect(hover_now); + if (is_layout_rtl() ^ (p_point.x <= tab_rect.position.x + tab_rect.size.width / 2)) { + if (hover_now > tab_from_id) { + hover_now -= 1; + } + } else if (tab_from_id > hover_now) { + hover_now += 1; + } + } else { + hover_now = is_layout_rtl() ^ (p_point.x < tab_bar->get_tab_rect(0).position.x) ? 0 : get_tab_count() - 1; } move_child(get_tab_control(tab_from_id), get_tab_control(hover_now)->get_index(false)); @@ -407,16 +423,31 @@ void TabContainer::_drop_data_fw(const Point2 &p_point, const Variant &p_data, C } else if (get_tabs_rearrange_group() != -1) { // Drag and drop between TabContainers. + Node *from_node = get_node(from_path); TabContainer *from_tabc = Object::cast_to<TabContainer>(from_node); + if (from_tabc && from_tabc->get_tabs_rearrange_group() == get_tabs_rearrange_group()) { + // Get the tab properties before they get erased by the child removal. + String tab_title = from_tabc->get_tab_title(tab_from_id); + bool tab_disabled = from_tabc->is_tab_disabled(tab_from_id); + + // Drop the new tab to the left or right depending on where the target tab is being hovered. + if (hover_now != -1) { + Rect2 tab_rect = tab_bar->get_tab_rect(hover_now); + if (is_layout_rtl() ^ (p_point.x > tab_rect.position.x + tab_rect.size.width / 2)) { + hover_now += 1; + } + } else { + hover_now = is_layout_rtl() ^ (p_point.x < tab_bar->get_tab_rect(0).position.x) ? 0 : get_tab_count(); + } + Control *moving_tabc = from_tabc->get_tab_control(tab_from_id); from_tabc->remove_child(moving_tabc); add_child(moving_tabc, true); - if (hover_now < 0) { - hover_now = get_tab_count() - 1; - } + set_tab_title(get_tab_count() - 1, tab_title); + set_tab_disabled(get_tab_count() - 1, tab_disabled); move_child(moving_tabc, get_tab_control(hover_now)->get_index(false)); if (!is_tab_disabled(hover_now)) { diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index da37228ed9..eb71401a3a 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -801,6 +801,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("increment_highlight", "TabContainer", icons["scroll_button_right_hl"]); theme->set_icon("decrement", "TabContainer", icons["scroll_button_left"]); theme->set_icon("decrement_highlight", "TabContainer", icons["scroll_button_left_hl"]); + theme->set_icon("drop_mark", "TabContainer", icons["tabs_drop_mark"]); theme->set_icon("menu", "TabContainer", icons["tabs_menu"]); theme->set_icon("menu_highlight", "TabContainer", icons["tabs_menu_hl"]); @@ -811,6 +812,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_unselected_color", "TabContainer", control_font_low_color); theme->set_color("font_disabled_color", "TabContainer", control_font_disabled_color); theme->set_color("font_outline_color", "TabContainer", Color(1, 1, 1)); + theme->set_color("drop_mark_color", "TabContainer", Color(1, 1, 1)); theme->set_constant("side_margin", "TabContainer", 8 * scale); theme->set_constant("icon_separation", "TabContainer", 4 * scale); @@ -828,6 +830,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("increment_highlight", "TabBar", icons["scroll_button_right_hl"]); theme->set_icon("decrement", "TabBar", icons["scroll_button_left"]); theme->set_icon("decrement_highlight", "TabBar", icons["scroll_button_left_hl"]); + theme->set_icon("drop_mark", "TabBar", icons["tabs_drop_mark"]); theme->set_icon("close", "TabBar", icons["close"]); theme->set_font("font", "TabBar", Ref<Font>()); @@ -837,6 +840,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_unselected_color", "TabBar", control_font_low_color); theme->set_color("font_disabled_color", "TabBar", control_font_disabled_color); theme->set_color("font_outline_color", "TabBar", Color(1, 1, 1)); + theme->set_color("drop_mark_color", "TabBar", Color(1, 1, 1)); theme->set_constant("hseparation", "TabBar", 4 * scale); theme->set_constant("outline_size", "TabBar", 0); diff --git a/scene/resources/default_theme/tabs_drop_mark.svg b/scene/resources/default_theme/tabs_drop_mark.svg new file mode 100644 index 0000000000..b1415bec45 --- /dev/null +++ b/scene/resources/default_theme/tabs_drop_mark.svg @@ -0,0 +1 @@ +<svg height="32" viewBox="0 0 16 32" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m5 1h6v30h-6z" fill="#d3d3d3"/></svg> |