Fix connecting a signal with a double click is too difficult

This commit is contained in:
Hilderin 2024-08-01 20:03:15 -04:00
parent 3978628c6c
commit f2dfa65b07
7 changed files with 87 additions and 39 deletions

View File

@ -899,9 +899,7 @@ Control *ConnectionsDockTree::make_custom_tooltip(const String &p_text) const {
return nullptr; return nullptr;
} }
EditorHelpBit *help_bit = memnew(EditorHelpBit(p_text)); return EditorHelpBitTooltip::show_tooltip(p_text, const_cast<ConnectionsDockTree *>(this));
EditorHelpBitTooltip::show_tooltip(help_bit, const_cast<ConnectionsDockTree *>(this));
return memnew(Control); // Make the standard tooltip invisible.
} }
struct _ConnectionsDockMethodInfoSort { struct _ConnectionsDockMethodInfoSort {

View File

@ -116,6 +116,8 @@ const Vector<String> packed_array_types = {
DocTools *EditorHelp::doc = nullptr; DocTools *EditorHelp::doc = nullptr;
DocTools *EditorHelp::ext_doc = nullptr; DocTools *EditorHelp::ext_doc = nullptr;
bool EditorHelpBitTooltip::_is_tooltip_visible = false;
static bool _attempt_doc_load(const String &p_class) { static bool _attempt_doc_load(const String &p_class) {
// Docgen always happens in the outer-most class: it also generates docs for inner classes. // Docgen always happens in the outer-most class: it also generates docs for inner classes.
String outer_class = p_class.get_slice(".", 0); String outer_class = p_class.get_slice(".", 0);
@ -3778,6 +3780,14 @@ void EditorHelpBitTooltip::_safe_queue_free() {
void EditorHelpBitTooltip::_target_gui_input(const Ref<InputEvent> &p_event) { void EditorHelpBitTooltip::_target_gui_input(const Ref<InputEvent> &p_event) {
const Ref<InputEventMouse> mouse_event = p_event; const Ref<InputEventMouse> mouse_event = p_event;
if (mouse_event.is_valid()) { 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<InputEventMouseMotion> mm = p_event;
if (mm.is_valid() && mm->get_relative().is_zero_approx()) {
return;
}
_start_timer(); _start_timer();
} }
} }
@ -3790,6 +3800,9 @@ void EditorHelpBitTooltip::_notification(int p_what) {
case NOTIFICATION_WM_MOUSE_EXIT: case NOTIFICATION_WM_MOUSE_EXIT:
_start_timer(); _start_timer();
break; break;
case NOTIFICATION_EXIT_TREE:
_is_tooltip_visible = false;
break;
} }
} }
@ -3811,14 +3824,36 @@ void EditorHelpBitTooltip::_input_from_window(const Ref<InputEvent> &p_event) {
} }
} }
void EditorHelpBitTooltip::show_tooltip(EditorHelpBit *p_help_bit, Control *p_target) { Control *EditorHelpBitTooltip::show_tooltip(const String &p_symbol, Control *p_target, const String &p_description) {
ERR_FAIL_NULL(p_help_bit); // 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)); EditorHelpBitTooltip *tooltip = memnew(EditorHelpBitTooltip(p_target));
p_help_bit->connect("request_hide", callable_mp(tooltip, &EditorHelpBitTooltip::_safe_queue_free)); help_bit->connect("request_hide", callable_mp(tooltip, &EditorHelpBitTooltip::_safe_queue_free));
tooltip->add_child(p_help_bit); tooltip->add_child(help_bit);
p_target->get_viewport()->add_child(tooltip); 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(); 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()`. // Copy-paste from `Viewport::_gui_show_tooltip()`.

View File

@ -325,6 +325,7 @@ public:
class EditorHelpBitTooltip : public PopupPanel { class EditorHelpBitTooltip : public PopupPanel {
GDCLASS(EditorHelpBitTooltip, PopupPanel); GDCLASS(EditorHelpBitTooltip, PopupPanel);
static bool _is_tooltip_visible;
Timer *timer = nullptr; Timer *timer = nullptr;
int _pushing_input = 0; int _pushing_input = 0;
bool _need_free = false; bool _need_free = false;
@ -332,13 +333,14 @@ class EditorHelpBitTooltip : public PopupPanel {
void _start_timer(); void _start_timer();
void _safe_queue_free(); void _safe_queue_free();
void _target_gui_input(const Ref<InputEvent> &p_event); void _target_gui_input(const Ref<InputEvent> &p_event);
static Control *_make_invisible_control();
protected: protected:
void _notification(int p_what); void _notification(int p_what);
virtual void _input_from_window(const Ref<InputEvent> &p_event) override; virtual void _input_from_window(const Ref<InputEvent> &p_event) override;
public: 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(); void popup_under_cursor();

View File

@ -991,30 +991,30 @@ Control *EditorProperty::make_custom_tooltip(const String &p_text) const {
} }
if (has_doc_tooltip || !custom_warning.is_empty()) { if (has_doc_tooltip || !custom_warning.is_empty()) {
EditorHelpBit *help_bit = memnew(EditorHelpBit); String symbol;
String description;
if (has_doc_tooltip) { if (has_doc_tooltip) {
help_bit->parse_symbol(p_text); symbol = p_text;
const EditorInspector *inspector = get_parent_inspector(); const EditorInspector *inspector = get_parent_inspector();
if (inspector) { if (inspector) {
const String custom_description = inspector->get_custom_property_description(p_text); const String custom_description = inspector->get_custom_property_description(p_text);
if (!custom_description.is_empty()) { if (!custom_description.is_empty()) {
help_bit->set_description(custom_description); description = custom_description;
} }
} }
} }
if (!custom_warning.is_empty()) { if (!custom_warning.is_empty()) {
String description = "[b][color=" + get_theme_color(SNAME("warning_color")).to_html(false) + "]" + custom_warning + "[/color][/b]"; const String custom_warning_description = "[b][color=" + get_theme_color(SNAME("warning_color")).to_html(false) + "]" + custom_warning + "[/color][/b]";
if (!help_bit->get_description().is_empty()) { if (description.is_empty()) {
description += "\n" + help_bit->get_description(); description = custom_warning_description;
} else {
description = custom_warning_description + "\n" + description;
} }
help_bit->set_description(description);
} }
EditorHelpBitTooltip::show_tooltip(help_bit, const_cast<EditorProperty *>(this)); return EditorHelpBitTooltip::show_tooltip(symbol, const_cast<EditorProperty *>(this), description);
return memnew(Control); // Make the standard tooltip invisible.
} }
return nullptr; return nullptr;
@ -1270,9 +1270,7 @@ Control *EditorInspectorCategory::make_custom_tooltip(const String &p_text) cons
return nullptr; return nullptr;
} }
EditorHelpBit *help_bit = memnew(EditorHelpBit(p_text)); return EditorHelpBitTooltip::show_tooltip(p_text, const_cast<EditorInspectorCategory *>(this));
EditorHelpBitTooltip::show_tooltip(help_bit, const_cast<EditorInspectorCategory *>(this));
return memnew(Control); // Make the standard tooltip invisible.
} }
Size2 EditorInspectorCategory::get_minimum_size() const { Size2 EditorInspectorCategory::get_minimum_size() const {

View File

@ -2267,9 +2267,7 @@ ThemeTypeDialog::ThemeTypeDialog() {
/////////////////////// ///////////////////////
Control *ThemeItemLabel::make_custom_tooltip(const String &p_text) const { Control *ThemeItemLabel::make_custom_tooltip(const String &p_text) const {
EditorHelpBit *help_bit = memnew(EditorHelpBit(p_text)); return EditorHelpBitTooltip::show_tooltip(p_text, const_cast<ThemeItemLabel *>(this));
EditorHelpBitTooltip::show_tooltip(help_bit, const_cast<ThemeItemLabel *>(this));
return memnew(Control); // Make the standard tooltip invisible.
} }
VBoxContainer *ThemeTypeEditor::_create_item_list(Theme::DataType p_data_type) { VBoxContainer *ThemeTypeEditor::_create_item_list(Theme::DataType p_data_type) {

View File

@ -4840,14 +4840,7 @@ void DisplayServerX11::process_events() {
WindowID window_id_other = INVALID_WINDOW_ID; WindowID window_id_other = INVALID_WINDOW_ID;
Window wd_other_x11_window; Window wd_other_x11_window;
if (wd.focused) { 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 {
// Propagate the event to the focused window, // Propagate the event to the focused window,
// because it's received only on the topmost window. // because it's received only on the topmost window.
// Note: This is needed for drag & drop to work between windows, // Note: This is needed for drag & drop to work between windows,
@ -4970,6 +4963,23 @@ void DisplayServerX11::process_events() {
pos = Point2i(windows[focused_window_id].size.width / 2, windows[focused_window_id].size.height / 2); pos = Point2i(windows[focused_window_id].size.width / 2, windows[focused_window_id].size.height / 2);
} }
BitField<MouseButtonMask> last_button_state = 0;
if (event.xmotion.state & Button1Mask) {
last_button_state.set_flag(MouseButtonMask::LEFT);
}
if (event.xmotion.state & Button2Mask) {
last_button_state.set_flag(MouseButtonMask::MIDDLE);
}
if (event.xmotion.state & Button3Mask) {
last_button_state.set_flag(MouseButtonMask::RIGHT);
}
if (event.xmotion.state & Button4Mask) {
last_button_state.set_flag(MouseButtonMask::MB_XBUTTON1);
}
if (event.xmotion.state & Button5Mask) {
last_button_state.set_flag(MouseButtonMask::MB_XBUTTON2);
}
Ref<InputEventMouseMotion> mm; Ref<InputEventMouseMotion> mm;
mm.instantiate(); mm.instantiate();
@ -4977,13 +4987,13 @@ void DisplayServerX11::process_events() {
if (xi.pressure_supported) { if (xi.pressure_supported) {
mm->set_pressure(xi.pressure); mm->set_pressure(xi.pressure);
} else { } else {
mm->set_pressure(bool(mouse_get_button_state().has_flag(MouseButtonMask::LEFT)) ? 1.0f : 0.0f); mm->set_pressure(bool(last_button_state.has_flag(MouseButtonMask::LEFT)) ? 1.0f : 0.0f);
} }
mm->set_tilt(xi.tilt); mm->set_tilt(xi.tilt);
mm->set_pen_inverted(xi.pen_inverted); mm->set_pen_inverted(xi.pen_inverted);
_get_key_modifier_state(event.xmotion.state, mm); _get_key_modifier_state(event.xmotion.state, mm);
mm->set_button_mask(mouse_get_button_state()); mm->set_button_mask(last_button_state);
mm->set_position(pos); mm->set_position(pos);
mm->set_global_position(pos); mm->set_global_position(pos);
mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity()); mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());

View File

@ -1457,6 +1457,17 @@ void Viewport::_gui_show_tooltip() {
return; 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. // Popup window which houses the tooltip content.
PopupPanel *panel = memnew(PopupPanel); PopupPanel *panel = memnew(PopupPanel);
panel->set_theme_type_variation(SNAME("TooltipPanel")); 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). // Ensure no opaque background behind the panel as its StyleBox can be partially transparent (e.g. corners).
panel->set_transparent_background(true); 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 no custom tooltip is given, use a default implementation.
if (!base_tooltip) { if (!base_tooltip) {
gui.tooltip_label = memnew(Label); gui.tooltip_label = memnew(Label);