diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 53b1f2a759a9..063e9b8b367a 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -911,9 +911,7 @@ 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. + return EditorHelpBitTooltip::show_tooltip(p_text, const_cast(this)); } struct _ConnectionsDockMethodInfoSort { diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 2def625f8ce7..d8f3a3565dbd 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -117,6 +117,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); @@ -3835,6 +3837,14 @@ 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()) { + // For some unknown reason, we receive mouse motion of zero when the tooltip + // is opened even if the mouse is not moving on Windows now that the toolip + // FLAG_POPUP is false. + Ref mm = p_event; + if (mm.is_valid() && mm->get_relative().is_zero_approx()) { + return; + } + _start_timer(); } } @@ -3847,6 +3857,9 @@ void EditorHelpBitTooltip::_notification(int p_what) { case NOTIFICATION_WM_MOUSE_EXIT: _start_timer(); break; + case NOTIFICATION_EXIT_TREE: + _is_tooltip_visible = false; + break; } } @@ -3868,14 +3881,36 @@ void EditorHelpBitTooltip::_input_from_window(const Ref &p_event) { } } -void EditorHelpBitTooltip::show_tooltip(EditorHelpBit *p_help_bit, Control *p_target) { - ERR_FAIL_NULL(p_help_bit); +Control *EditorHelpBitTooltip::show_tooltip(const String &p_symbol, Control *p_target, const String &p_description) { + // Show the custom tooltip only if it is not already visible. + // The Viewport will retrigger make_custom_tooltip every few seconds + // because the return control is not visible even if the custom tooltip is displayed. + if (_is_tooltip_visible) { + return _make_invisible_control(); + } + + EditorHelpBit *help_bit = memnew(EditorHelpBit(p_symbol)); + if (!p_description.is_empty()) { + help_bit->set_description(p_description); + } 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); + help_bit->connect("request_hide", callable_mp(tooltip, &EditorHelpBitTooltip::_safe_queue_free)); + tooltip->add_child(help_bit); p_target->add_child(tooltip); - p_help_bit->update_content_height(); + help_bit->update_content_height(); + // When FLAG_POPUP is false, it prevents the editor from losing focus when displaying the tooltip. + // This way, clicks and double-clicks are still available outside the tooltip. + tooltip->set_flag(Window::FLAG_POPUP, false); tooltip->popup_under_cursor(); + _is_tooltip_visible = true; + + return _make_invisible_control(); +} + +Control *EditorHelpBitTooltip::_make_invisible_control() { + Control *control = memnew(Control); + control->set_visible(false); + return control; } // Copy-paste from `Viewport::_gui_show_tooltip()`. diff --git a/editor/editor_help.h b/editor/editor_help.h index 93f74cb2c123..b031cfb86c4d 100644 --- a/editor/editor_help.h +++ b/editor/editor_help.h @@ -325,6 +325,7 @@ 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; @@ -332,13 +333,14 @@ class EditorHelpBitTooltip : public PopupPanel { void _start_timer(); void _safe_queue_free(); void _target_gui_input(const Ref &p_event); + static Control *_make_invisible_control(); protected: void _notification(int p_what); virtual void _input_from_window(const Ref &p_event) override; public: - static void show_tooltip(EditorHelpBit *p_help_bit, Control *p_target); + static Control *show_tooltip(const String &p_symbol, Control *p_target, const String &p_description = String()); void popup_under_cursor(); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index f5d1c1115437..8fa3de67e651 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -1011,30 +1011,30 @@ Control *EditorProperty::make_custom_tooltip(const String &p_text) const { } if (has_doc_tooltip || !custom_warning.is_empty()) { - EditorHelpBit *help_bit = memnew(EditorHelpBit); - + String symbol; + String description; if (has_doc_tooltip) { - help_bit->parse_symbol(p_text); + symbol = p_text; const EditorInspector *inspector = get_parent_inspector(); if (inspector) { const String custom_description = inspector->get_custom_property_description(p_text); if (!custom_description.is_empty()) { - help_bit->set_description(custom_description); + description = custom_description; } } } if (!custom_warning.is_empty()) { - String description = "[b][color=" + get_theme_color(SNAME("warning_color")).to_html(false) + "]" + custom_warning + "[/color][/b]"; - if (!help_bit->get_description().is_empty()) { - description += "\n" + help_bit->get_description(); + const String custom_warning_description = "[b][color=" + get_theme_color(SNAME("warning_color")).to_html(false) + "]" + custom_warning + "[/color][/b]"; + if (description.is_empty()) { + description = custom_warning_description; + } else { + description = custom_warning_description + "\n" + description; } - help_bit->set_description(description); } - EditorHelpBitTooltip::show_tooltip(help_bit, const_cast(this)); - return memnew(Control); // Make the standard tooltip invisible. + return EditorHelpBitTooltip::show_tooltip(symbol, const_cast(this), description); } return nullptr; @@ -1331,9 +1331,7 @@ Control *EditorInspectorCategory::make_custom_tooltip(const String &p_text) cons 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. + return EditorHelpBitTooltip::show_tooltip(p_text, const_cast(this)); } void EditorInspectorCategory::set_as_favorite(EditorInspector *p_for_inspector) { diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index c2f6896e3dcc..93bfe21be677 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -2284,9 +2284,7 @@ ThemeTypeDialog::ThemeTypeDialog() { /////////////////////// Control *ThemeItemLabel::make_custom_tooltip(const String &p_text) const { - EditorHelpBit *help_bit = memnew(EditorHelpBit(p_text)); - EditorHelpBitTooltip::show_tooltip(help_bit, const_cast(this)); - return memnew(Control); // Make the standard tooltip invisible. + return EditorHelpBitTooltip::show_tooltip(p_text, const_cast(this)); } VBoxContainer *ThemeTypeEditor::_create_item_list(Theme::DataType p_data_type) { diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index 90bd3d57d4b7..be66ea30ae38 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -4857,14 +4857,7 @@ void DisplayServerX11::process_events() { WindowID window_id_other = INVALID_WINDOW_ID; Window wd_other_x11_window; - if (wd.focused) { - // Handle cases where an unfocused popup is open that needs to receive button-up events. - WindowID popup_id = _get_focused_window_or_popup(); - if (popup_id != INVALID_WINDOW_ID && popup_id != window_id) { - window_id_other = popup_id; - wd_other_x11_window = windows[popup_id].x11_window; - } - } else { + if (!wd.focused) { // Propagate the event to the focused window, // because it's received only on the topmost window. // Note: This is needed for drag & drop to work between windows, diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 1a047dfa2765..e4ad96536b0a 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1455,7 +1455,14 @@ void Viewport::_gui_show_tooltip() { // 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 ? tooltip_owner->make_custom_tooltip(gui.tooltip_text) : nullptr; + 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; + } if (gui.tooltip_text.is_empty() && !base_tooltip) { return; // Nothing to show.