3333#include " scene/gui/box_container.h"
3434#include " scene/gui/label.h"
3535#include " scene/gui/texture_rect.h"
36+ #include " scene/main/timer.h"
3637#include " scene/main/viewport.h"
3738#include " scene/theme/theme_db.h"
3839
@@ -501,6 +502,13 @@ void TabBar::_notification(int p_what) {
501502 dragging_valid_tab = false ;
502503 queue_redraw ();
503504 }
505+ [[fallthrough]];
506+ }
507+
508+ case NOTIFICATION_MOUSE_EXIT: {
509+ if (!hover_switch_delay->is_stopped ()) {
510+ hover_switch_delay->stop ();
511+ }
504512 } break ;
505513
506514 case NOTIFICATION_DRAW: {
@@ -1285,6 +1293,10 @@ void TabBar::_update_cache(bool p_update_hover) {
12851293 }
12861294}
12871295
1296+ void TabBar::_hover_switch_timeout () {
1297+ set_current_tab (hover);
1298+ }
1299+
12881300void TabBar::_on_mouse_exited () {
12891301 rb_hover = -1 ;
12901302 cb_hover = -1 ;
@@ -1424,6 +1436,10 @@ Variant TabBar::get_drag_data(const Point2 &p_point) {
14241436}
14251437
14261438bool TabBar::can_drop_data (const Point2 &p_point, const Variant &p_data) const {
1439+ if (switch_on_drag_hover) {
1440+ _handle_switch_on_hover (p_data);
1441+ }
1442+
14271443 bool drop_override = Control::can_drop_data (p_point, p_data);
14281444 if (drop_override) {
14291445 return drop_override;
@@ -1470,7 +1486,8 @@ Variant TabBar::_handle_get_drag_data(const String &p_type, const Point2 &p_poin
14701486 set_drag_preview (drag_preview);
14711487
14721488 Dictionary drag_data;
1473- drag_data[" type" ] = p_type;
1489+ drag_data[" type" ] = " tab" ;
1490+ drag_data[" tab_type" ] = p_type;
14741491 drag_data[" tab_index" ] = tab_over;
14751492 drag_data[" from_path" ] = get_path ();
14761493
@@ -1479,11 +1496,12 @@ Variant TabBar::_handle_get_drag_data(const String &p_type, const Point2 &p_poin
14791496
14801497bool TabBar::_handle_can_drop_data (const String &p_type, const Point2 &p_point, const Variant &p_data) const {
14811498 Dictionary d = p_data;
1482- if (!d. has (" type" ) ) {
1499+ if (d. get (" type" , " " ). operator String () != " tab " ) {
14831500 return false ;
14841501 }
14851502
1486- if (String (d[" type" ]) == p_type) {
1503+ const String tab_type = d.get (" tab_type" , " " );
1504+ if (tab_type == p_type) {
14871505 NodePath from_path = d[" from_path" ];
14881506 NodePath to_path = get_path ();
14891507 if (from_path == to_path) {
@@ -1497,17 +1515,17 @@ bool TabBar::_handle_can_drop_data(const String &p_type, const Point2 &p_point,
14971515 }
14981516 }
14991517 }
1500-
15011518 return false ;
15021519}
15031520
15041521void TabBar::_handle_drop_data (const String &p_type, const Point2 &p_point, const Variant &p_data, const Callable &p_move_tab_callback, const Callable &p_move_tab_from_other_callback) {
15051522 Dictionary d = p_data;
1506- if (!d. has (" type" ) ) {
1523+ if (d. get (" type" , " " ). operator String () != " tab " ) {
15071524 return ;
15081525 }
15091526
1510- if (String (d[" type" ]) == p_type) {
1527+ const String tab_type = d.get (" tab_type" , " " );
1528+ if (tab_type == p_type) {
15111529 int tab_from_id = d[" tab_index" ];
15121530 int hover_now = (p_point == Vector2 (Math::INF, Math::INF)) ? current : get_closest_tab_idx_to_point (p_point);
15131531 NodePath from_path = d[" from_path" ];
@@ -1565,6 +1583,22 @@ void TabBar::_handle_drop_data(const String &p_type, const Point2 &p_point, cons
15651583 }
15661584}
15671585
1586+ void TabBar::_handle_switch_on_hover (const Variant &p_data) const {
1587+ Dictionary d = p_data;
1588+ if (d.get (" type" , " " ).operator String () == " tab" ) {
1589+ // Dragging a tab shouldn't switch on hover.
1590+ return ;
1591+ }
1592+
1593+ if (hover > -1 && hover != current) {
1594+ if (hover_switch_delay->is_stopped ()) {
1595+ const_cast <TabBar *>(this )->hover_switch_delay ->start (theme_cache.hover_switch_wait_msec * 0.001 );
1596+ }
1597+ } else if (!hover_switch_delay->is_stopped ()) {
1598+ hover_switch_delay->stop ();
1599+ }
1600+ }
1601+
15681602void TabBar::_move_tab_from (TabBar *p_from_tabbar, int p_from_index, int p_to_index) {
15691603 Tab moving_tab = p_from_tabbar->tabs [p_from_index];
15701604 moving_tab.accessibility_item_element = RID ();
@@ -1983,6 +2017,14 @@ bool TabBar::get_scroll_to_selected() const {
19832017 return scroll_to_selected;
19842018}
19852019
2020+ void TabBar::set_switch_on_drag_hover (bool p_enabled) {
2021+ switch_on_drag_hover = p_enabled;
2022+ }
2023+
2024+ bool TabBar::get_switch_on_drag_hover () const {
2025+ return switch_on_drag_hover;
2026+ }
2027+
19862028void TabBar::set_select_with_rmb (bool p_enabled) {
19872029 select_with_rmb = p_enabled;
19882030}
@@ -2055,6 +2097,8 @@ void TabBar::_bind_methods() {
20552097 ClassDB::bind_method (D_METHOD (" get_scrolling_enabled" ), &TabBar::get_scrolling_enabled);
20562098 ClassDB::bind_method (D_METHOD (" set_drag_to_rearrange_enabled" , " enabled" ), &TabBar::set_drag_to_rearrange_enabled);
20572099 ClassDB::bind_method (D_METHOD (" get_drag_to_rearrange_enabled" ), &TabBar::get_drag_to_rearrange_enabled);
2100+ ClassDB::bind_method (D_METHOD (" set_switch_on_drag_hover" , " enabled" ), &TabBar::set_switch_on_drag_hover);
2101+ ClassDB::bind_method (D_METHOD (" get_switch_on_drag_hover" ), &TabBar::get_switch_on_drag_hover);
20582102 ClassDB::bind_method (D_METHOD (" set_tabs_rearrange_group" , " group_id" ), &TabBar::set_tabs_rearrange_group);
20592103 ClassDB::bind_method (D_METHOD (" get_tabs_rearrange_group" ), &TabBar::get_tabs_rearrange_group);
20602104 ClassDB::bind_method (D_METHOD (" set_scroll_to_selected" , " enabled" ), &TabBar::set_scroll_to_selected);
@@ -2082,6 +2126,7 @@ void TabBar::_bind_methods() {
20822126 ADD_PROPERTY (PropertyInfo (Variant::INT, " max_tab_width" , PROPERTY_HINT_RANGE, " 0,99999,1,suffix:px" ), " set_max_tab_width" , " get_max_tab_width" );
20832127 ADD_PROPERTY (PropertyInfo (Variant::BOOL, " scrolling_enabled" ), " set_scrolling_enabled" , " get_scrolling_enabled" );
20842128 ADD_PROPERTY (PropertyInfo (Variant::BOOL, " drag_to_rearrange_enabled" ), " set_drag_to_rearrange_enabled" , " get_drag_to_rearrange_enabled" );
2129+ ADD_PROPERTY (PropertyInfo (Variant::BOOL, " switch_on_drag_hover" ), " set_switch_on_drag_hover" , " get_switch_on_drag_hover" );
20852130 ADD_PROPERTY (PropertyInfo (Variant::INT, " tabs_rearrange_group" ), " set_tabs_rearrange_group" , " get_tabs_rearrange_group" );
20862131 ADD_PROPERTY (PropertyInfo (Variant::BOOL, " scroll_to_selected" ), " set_scroll_to_selected" , " get_scroll_to_selected" );
20872132 ADD_PROPERTY (PropertyInfo (Variant::BOOL, " select_with_rmb" ), " set_select_with_rmb" , " get_select_with_rmb" );
@@ -2102,6 +2147,7 @@ void TabBar::_bind_methods() {
21022147 BIND_THEME_ITEM (Theme::DATA_TYPE_CONSTANT, TabBar, h_separation);
21032148 BIND_THEME_ITEM (Theme::DATA_TYPE_CONSTANT, TabBar, tab_separation);
21042149 BIND_THEME_ITEM (Theme::DATA_TYPE_CONSTANT, TabBar, icon_max_width);
2150+ BIND_THEME_ITEM (Theme::DATA_TYPE_CONSTANT, TabBar, hover_switch_wait_msec);
21052151
21062152 BIND_THEME_ITEM_CUSTOM (Theme::DATA_TYPE_STYLEBOX, TabBar, tab_unselected_style, " tab_unselected" );
21072153 BIND_THEME_ITEM_CUSTOM (Theme::DATA_TYPE_STYLEBOX, TabBar, tab_hovered_style, " tab_hovered" );
@@ -2152,5 +2198,10 @@ TabBar::TabBar() {
21522198 set_focus_mode (FOCUS_ALL);
21532199 connect (SceneStringName (mouse_exited), callable_mp (this , &TabBar::_on_mouse_exited));
21542200
2201+ hover_switch_delay = memnew (Timer);
2202+ hover_switch_delay->connect (" timeout" , callable_mp (this , &TabBar::_hover_switch_timeout));
2203+ hover_switch_delay->set_one_shot (true );
2204+ add_child (hover_switch_delay, false , INTERNAL_MODE_FRONT);
2205+
21552206 property_helper.setup_for_instance (base_property_helper, this );
21562207}
0 commit comments