diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index eb0ab1174b32..edbea30ee7de 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -899,9 +899,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 637c39c8ec35..8cfd057ba1f4 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,6 +3780,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(); } } @@ -3790,6 +3800,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 +3824,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->get_viewport()->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 199383c3918b..9e43b653ae4b 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -991,30 +991,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; @@ -1270,9 +1270,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)); } Size2 EditorInspectorCategory::get_minimum_size() const { diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index 99635a25319c..196bda26f319 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -2267,9 +2267,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 4a52e2637325..1b3ae8b4fa9c 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -4842,10 +4842,12 @@ void DisplayServerX11::process_events() { 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; + if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { + 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 { // Propagate the event to the focused window, 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);