diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index eb0ab1174b32..0fc4f19e9f07 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -899,9 +899,30 @@ Control *ConnectionsDockTree::make_custom_tooltip(const String &p_text) const { return nullptr; } - EditorHelpBit *help_bit = memnew(EditorHelpBit(p_text)); - EditorHelpBitTooltip::show_tooltip(help_bit, const_cast(this)); - return memnew(Control); // Make the standard tooltip invisible. + // Show the custom tooltip only if it is not already visible. With the parameter p_popup_flag set to false, + // the editor does not lose focus when showing the tooltip, and the Viewport will + // retrigger make_custom_tooltip every few seconds even if the custom tooltip is displayed. + if (!EditorHelpBitTooltip::is_tooltip_visible()) { + EditorHelpBit *help_bit = memnew(EditorHelpBit(p_text)); + EditorHelpBitTooltip::show_tooltip(help_bit, const_cast(this), false); + } + + return _invisible_tooltip; +} + +ConnectionsDockTree::ConnectionsDockTree() { + // Make the standard tooltip invisible. + // Ensure that the control is not visible so the default tooltip would + // not show at the same time as the custom one. The control is created only one time + // because the viewport will try to recreate the tooltip each couple of seconds + // add recall make_custom_tooltip each time because it thinks that there's not tooltip. + // This prevent to recreate a new control each time not nothing. + _invisible_tooltip = memnew(Control); + _invisible_tooltip->set_visible(false); +} + +ConnectionsDockTree::~ConnectionsDockTree() { + memdelete(_invisible_tooltip); } struct _ConnectionsDockMethodInfoSort { diff --git a/editor/connections_dialog.h b/editor/connections_dialog.h index 78f1b76e2353..e6b0f2aa021c 100644 --- a/editor/connections_dialog.h +++ b/editor/connections_dialog.h @@ -193,7 +193,15 @@ class ConnectDialog : public ConfirmationDialog { // Custom `Tree` needed to use `EditorHelpBit` to display signal documentation. class ConnectionsDockTree : public Tree { +private: + Control *_invisible_tooltip; + +protected: virtual Control *make_custom_tooltip(const String &p_text) const; + +public: + ConnectionsDockTree(); + ~ConnectionsDockTree(); }; class ConnectionsDock : public VBoxContainer { diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 637c39c8ec35..6bd252d11786 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -116,6 +116,8 @@ const Vector packed_array_types = { DocTools *EditorHelp::doc = nullptr; DocTools *EditorHelp::ext_doc = nullptr; +bool EditorHelpBitTooltip::_is_tooltip_visible = false; + static bool _attempt_doc_load(const String &p_class) { // Docgen always happens in the outer-most class: it also generates docs for inner classes. String outer_class = p_class.get_slice(".", 0); @@ -3778,7 +3780,11 @@ void EditorHelpBitTooltip::_safe_queue_free() { void EditorHelpBitTooltip::_target_gui_input(const Ref &p_event) { const Ref mouse_event = p_event; if (mouse_event.is_valid()) { - _start_timer(); + if (!_first_mouse_event_done) { + _first_mouse_event_done = true; + } else { + _start_timer(); + } } } @@ -3790,6 +3796,9 @@ void EditorHelpBitTooltip::_notification(int p_what) { case NOTIFICATION_WM_MOUSE_EXIT: _start_timer(); break; + case NOTIFICATION_EXIT_TREE: + _is_tooltip_visible = false; + break; } } @@ -3811,14 +3820,22 @@ void EditorHelpBitTooltip::_input_from_window(const Ref &p_event) { } } -void EditorHelpBitTooltip::show_tooltip(EditorHelpBit *p_help_bit, Control *p_target) { +void EditorHelpBitTooltip::show_tooltip(EditorHelpBit *p_help_bit, Control *p_target, bool p_popup_flag) { ERR_FAIL_NULL(p_help_bit); EditorHelpBitTooltip *tooltip = memnew(EditorHelpBitTooltip(p_target)); p_help_bit->connect("request_hide", callable_mp(tooltip, &EditorHelpBitTooltip::_safe_queue_free)); tooltip->add_child(p_help_bit); p_target->get_viewport()->add_child(tooltip); p_help_bit->update_content_height(); + // When FLAG_POPUP is false, it prevents the editor from losing focus when displaying the tooltip. + // This way, double-clicks are still available when showing tooltips. + tooltip->set_flag(Window::FLAG_POPUP, p_popup_flag); tooltip->popup_under_cursor(); + _is_tooltip_visible = true; +} + +bool EditorHelpBitTooltip::is_tooltip_visible() { + return _is_tooltip_visible; } // Copy-paste from `Viewport::_gui_show_tooltip()`. @@ -3858,6 +3875,12 @@ void EditorHelpBitTooltip::popup_under_cursor() { r.position.y = vr.position.y; } + // Reset the flag for the first mouse event. + // When FLAG_POPUP is false, there's always a first _target_gui_input event + // triggered when opening the popup and it's important to discard it, + // otherwise, the timer would be starter and the tooltip would close. + _first_mouse_event_done = false; + set_flag(Window::FLAG_NO_FOCUS, true); popup(r); } diff --git a/editor/editor_help.h b/editor/editor_help.h index 93f74cb2c123..f2221b0e1b60 100644 --- a/editor/editor_help.h +++ b/editor/editor_help.h @@ -325,9 +325,11 @@ class EditorHelpBit : public VBoxContainer { class EditorHelpBitTooltip : public PopupPanel { GDCLASS(EditorHelpBitTooltip, PopupPanel); + static bool _is_tooltip_visible; Timer *timer = nullptr; int _pushing_input = 0; bool _need_free = false; + bool _first_mouse_event_done = false; void _start_timer(); void _safe_queue_free(); @@ -338,7 +340,8 @@ class EditorHelpBitTooltip : public PopupPanel { virtual void _input_from_window(const Ref &p_event) override; public: - static void show_tooltip(EditorHelpBit *p_help_bit, Control *p_target); + static void show_tooltip(EditorHelpBit *p_help_bit, Control *p_target, bool p_popup_flag = true); + static bool is_tooltip_visible(); void popup_under_cursor(); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 1302e3c53e66..ba83324e02ce 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1457,6 +1457,17 @@ void Viewport::_gui_show_tooltip() { return; } + // Controls can implement `make_custom_tooltip` to provide their own tooltip. + // This should be a Control node which will be added as child to a TooltipPanel. + Control *base_tooltip = tooltip_owner->make_custom_tooltip(gui.tooltip_text); + + // When the custom control is not visible, don't show any tooltip. + // This way, the custom tooltip from ConnectionsDockTree can create + // its own tooltip without conflicting with the default one, even an empty tooltip. + if (base_tooltip && !base_tooltip->is_visible()) { + return; + } + // Popup window which houses the tooltip content. PopupPanel *panel = memnew(PopupPanel); panel->set_theme_type_variation(SNAME("TooltipPanel")); @@ -1464,10 +1475,6 @@ void Viewport::_gui_show_tooltip() { // Ensure no opaque background behind the panel as its StyleBox can be partially transparent (e.g. corners). panel->set_transparent_background(true); - // Controls can implement `make_custom_tooltip` to provide their own tooltip. - // This should be a Control node which will be added as child to a TooltipPanel. - Control *base_tooltip = tooltip_owner->make_custom_tooltip(gui.tooltip_text); - // If no custom tooltip is given, use a default implementation. if (!base_tooltip) { gui.tooltip_label = memnew(Label);