Skip to content

Commit 20064cf

Browse files
committed
Merge pull request #95044 from Hilderin/fix-double-click-signal-connection
Fix connecting a signal with a double click is too difficult
2 parents f7f2361 + 4e19ab8 commit 20064cf

File tree

9 files changed

+147
-98
lines changed

9 files changed

+147
-98
lines changed

core/input/input.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,26 @@ bool Input::is_anything_pressed() const {
308308
return false;
309309
}
310310

311+
bool Input::is_anything_pressed_except_mouse() const {
312+
_THREAD_SAFE_METHOD_
313+
314+
if (disable_input) {
315+
return false;
316+
}
317+
318+
if (!keys_pressed.is_empty() || !joy_buttons_pressed.is_empty()) {
319+
return true;
320+
}
321+
322+
for (const KeyValue<StringName, Input::ActionState> &E : action_states) {
323+
if (E.value.cache.pressed) {
324+
return true;
325+
}
326+
}
327+
328+
return false;
329+
}
330+
311331
bool Input::is_key_pressed(Key p_keycode) const {
312332
_THREAD_SAFE_METHOD_
313333

core/input/input.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ class Input : public Object {
294294
static Input *get_singleton();
295295

296296
bool is_anything_pressed() const;
297+
bool is_anything_pressed_except_mouse() const;
297298
bool is_key_pressed(Key p_keycode) const;
298299
bool is_physical_key_pressed(Key p_keycode) const;
299300
bool is_key_label_pressed(Key p_keycode) const;

editor/connections_dialog.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -911,9 +911,7 @@ Control *ConnectionsDockTree::make_custom_tooltip(const String &p_text) const {
911911
return nullptr;
912912
}
913913

914-
EditorHelpBit *help_bit = memnew(EditorHelpBit(p_text));
915-
EditorHelpBitTooltip::show_tooltip(help_bit, const_cast<ConnectionsDockTree *>(this));
916-
return memnew(Control); // Make the standard tooltip invisible.
914+
return EditorHelpBitTooltip::show_tooltip(const_cast<ConnectionsDockTree *>(this), p_text);
917915
}
918916

919917
struct _ConnectionsDockMethodInfoSort {

editor/editor_help.cpp

Lines changed: 86 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3690,7 +3690,7 @@ void EditorHelpBit::_notification(int p_what) {
36903690
}
36913691
}
36923692

3693-
void EditorHelpBit::parse_symbol(const String &p_symbol) {
3693+
void EditorHelpBit::parse_symbol(const String &p_symbol, const String &p_prologue) {
36943694
const PackedStringArray slices = p_symbol.split("|", true, 2);
36953695
ERR_FAIL_COND_MSG(slices.size() < 3, "Invalid doc id. The expected format is 'item_type|class_name|item_name'.");
36963696

@@ -3737,6 +3737,14 @@ void EditorHelpBit::parse_symbol(const String &p_symbol) {
37373737
symbol_visible_type = visible_type;
37383738
symbol_name = name;
37393739

3740+
if (!p_prologue.is_empty()) {
3741+
if (help_data.description.is_empty()) {
3742+
help_data.description = p_prologue;
3743+
} else {
3744+
help_data.description = p_prologue + "\n" + help_data.description;
3745+
}
3746+
}
3747+
37403748
if (help_data.description.is_empty()) {
37413749
help_data.description = "[color=<EditorHelpBitCommentColor>][i]" + TTR("No description available.") + "[/i][/color]";
37423750
}
@@ -3760,14 +3768,6 @@ void EditorHelpBit::set_custom_text(const String &p_type, const String &p_name,
37603768
}
37613769
}
37623770

3763-
void EditorHelpBit::set_description(const String &p_text) {
3764-
help_data.description = p_text;
3765-
3766-
if (is_inside_tree()) {
3767-
_update_labels();
3768-
}
3769-
}
3770-
37713771
void EditorHelpBit::set_content_height_limits(float p_min, float p_max) {
37723772
ERR_FAIL_COND(p_min > p_max);
37733773
content_min_height = p_min;
@@ -3787,15 +3787,15 @@ void EditorHelpBit::update_content_height() {
37873787
content->set_custom_minimum_size(Size2(content->get_custom_minimum_size().x, CLAMP(content_height, content_min_height, content_max_height)));
37883788
}
37893789

3790-
EditorHelpBit::EditorHelpBit(const String &p_symbol) {
3790+
EditorHelpBit::EditorHelpBit(const String &p_symbol, const String &p_prologue, bool p_allow_selection) {
37913791
add_theme_constant_override("separation", 0);
37923792

37933793
title = memnew(RichTextLabel);
37943794
title->set_theme_type_variation("EditorHelpBitTitle");
37953795
title->set_custom_minimum_size(Size2(512 * EDSCALE, 0)); // GH-93031. Set the minimum width even if `fit_content` is true.
37963796
title->set_fit_content(true);
3797-
title->set_selection_enabled(true);
3798-
//title->set_context_menu_enabled(true); // TODO: Fix opening context menu hides tooltip.
3797+
title->set_selection_enabled(p_allow_selection);
3798+
title->set_context_menu_enabled(p_allow_selection);
37993799
title->connect("meta_clicked", callable_mp(this, &EditorHelpBit::_meta_clicked));
38003800
title->hide();
38013801
add_child(title);
@@ -3806,76 +3806,108 @@ EditorHelpBit::EditorHelpBit(const String &p_symbol) {
38063806
content = memnew(RichTextLabel);
38073807
content->set_theme_type_variation("EditorHelpBitContent");
38083808
content->set_custom_minimum_size(Size2(512 * EDSCALE, content_min_height));
3809-
content->set_selection_enabled(true);
3810-
//content->set_context_menu_enabled(true); // TODO: Fix opening context menu hides tooltip.
3809+
content->set_selection_enabled(p_allow_selection);
3810+
content->set_context_menu_enabled(p_allow_selection);
38113811
content->connect("meta_clicked", callable_mp(this, &EditorHelpBit::_meta_clicked));
38123812
add_child(content);
38133813

38143814
if (!p_symbol.is_empty()) {
3815-
parse_symbol(p_symbol);
3815+
parse_symbol(p_symbol, p_prologue);
38163816
}
38173817
}
38183818

38193819
/// EditorHelpBitTooltip ///
38203820

3821+
bool EditorHelpBitTooltip::_is_tooltip_visible = false;
3822+
3823+
Control *EditorHelpBitTooltip::_make_invisible_control() {
3824+
Control *control = memnew(Control);
3825+
control->set_visible(false);
3826+
return control;
3827+
}
3828+
38213829
void EditorHelpBitTooltip::_start_timer() {
38223830
if (timer->is_inside_tree() && timer->is_stopped()) {
38233831
timer->start();
38243832
}
38253833
}
38263834

3827-
void EditorHelpBitTooltip::_safe_queue_free() {
3828-
if (_pushing_input > 0) {
3829-
_need_free = true;
3830-
} else {
3831-
queue_free();
3832-
}
3833-
}
3834-
38353835
void EditorHelpBitTooltip::_target_gui_input(const Ref<InputEvent> &p_event) {
3836-
const Ref<InputEventMouse> mouse_event = p_event;
3837-
if (mouse_event.is_valid()) {
3838-
_start_timer();
3836+
// Only scrolling is not checked in `NOTIFICATION_INTERNAL_PROCESS`.
3837+
const Ref<InputEventMouseButton> mb = p_event;
3838+
if (mb.is_valid()) {
3839+
switch (mb->get_button_index()) {
3840+
case MouseButton::WHEEL_UP:
3841+
case MouseButton::WHEEL_DOWN:
3842+
case MouseButton::WHEEL_LEFT:
3843+
case MouseButton::WHEEL_RIGHT:
3844+
queue_free();
3845+
break;
3846+
default:
3847+
break;
3848+
}
38393849
}
38403850
}
38413851

38423852
void EditorHelpBitTooltip::_notification(int p_what) {
38433853
switch (p_what) {
3854+
case NOTIFICATION_ENTER_TREE:
3855+
_is_tooltip_visible = true;
3856+
_enter_tree_time = OS::get_singleton()->get_ticks_msec();
3857+
break;
3858+
case NOTIFICATION_EXIT_TREE:
3859+
_is_tooltip_visible = false;
3860+
break;
38443861
case NOTIFICATION_WM_MOUSE_ENTER:
3862+
_is_mouse_inside_tooltip = true;
38453863
timer->stop();
38463864
break;
38473865
case NOTIFICATION_WM_MOUSE_EXIT:
3866+
_is_mouse_inside_tooltip = false;
38483867
_start_timer();
38493868
break;
3869+
case NOTIFICATION_INTERNAL_PROCESS:
3870+
// A workaround to hide the tooltip since the window does not receive keyboard events
3871+
// with `FLAG_POPUP` and `FLAG_NO_FOCUS` flags, so we can't use `_input_from_window()`.
3872+
if (is_inside_tree()) {
3873+
if (Input::get_singleton()->is_action_just_pressed(SNAME("ui_cancel"), true)) {
3874+
queue_free();
3875+
get_parent_viewport()->set_input_as_handled();
3876+
} else if (Input::get_singleton()->is_anything_pressed_except_mouse()) {
3877+
queue_free();
3878+
} else if (!Input::get_singleton()->get_mouse_button_mask().is_empty()) {
3879+
if (!_is_mouse_inside_tooltip) {
3880+
queue_free();
3881+
}
3882+
} else if (!Input::get_singleton()->get_last_mouse_velocity().is_zero_approx()) {
3883+
if (!_is_mouse_inside_tooltip && OS::get_singleton()->get_ticks_msec() - _enter_tree_time > 250) {
3884+
_start_timer();
3885+
}
3886+
}
3887+
}
3888+
break;
38503889
}
38513890
}
38523891

3853-
// Forwards non-mouse input to the parent viewport.
3854-
void EditorHelpBitTooltip::_input_from_window(const Ref<InputEvent> &p_event) {
3855-
if (p_event->is_action_pressed(SNAME("ui_cancel"), false, true)) {
3856-
_safe_queue_free();
3857-
} else {
3858-
const Ref<InputEventMouse> mouse_event = p_event;
3859-
if (mouse_event.is_null()) {
3860-
// GH-91652. Prevents use-after-free since `ProgressDialog` calls `Main::iteration()`.
3861-
_pushing_input++;
3862-
get_parent_viewport()->push_input(p_event);
3863-
_pushing_input--;
3864-
if (_pushing_input <= 0 && _need_free) {
3865-
queue_free();
3866-
}
3867-
}
3892+
Control *EditorHelpBitTooltip::show_tooltip(Control *p_target, const String &p_symbol, const String &p_prologue) {
3893+
// Show the custom tooltip only if it is not already visible.
3894+
// The viewport will retrigger `make_custom_tooltip()` every few seconds
3895+
// because the return control is not visible even if the custom tooltip is displayed.
3896+
if (_is_tooltip_visible || Input::get_singleton()->is_anything_pressed()) {
3897+
return _make_invisible_control();
38683898
}
3869-
}
38703899

3871-
void EditorHelpBitTooltip::show_tooltip(EditorHelpBit *p_help_bit, Control *p_target) {
3872-
ERR_FAIL_NULL(p_help_bit);
3900+
EditorHelpBit *help_bit = memnew(EditorHelpBit(p_symbol, p_prologue, false));
3901+
38733902
EditorHelpBitTooltip *tooltip = memnew(EditorHelpBitTooltip(p_target));
3874-
p_help_bit->connect("request_hide", callable_mp(tooltip, &EditorHelpBitTooltip::_safe_queue_free));
3875-
tooltip->add_child(p_help_bit);
3903+
help_bit->connect("request_hide", callable_mp(static_cast<Node *>(tooltip), &Node::queue_free));
3904+
tooltip->add_child(help_bit);
38763905
p_target->add_child(tooltip);
3877-
p_help_bit->update_content_height();
3906+
3907+
help_bit->update_content_height();
38783908
tooltip->popup_under_cursor();
3909+
3910+
return _make_invisible_control();
38793911
}
38803912

38813913
// Copy-paste from `Viewport::_gui_show_tooltip()`.
@@ -3915,6 +3947,9 @@ void EditorHelpBitTooltip::popup_under_cursor() {
39153947
r.position.y = vr.position.y;
39163948
}
39173949

3950+
// When `FLAG_POPUP` is false, it prevents the editor from losing focus when displaying the tooltip.
3951+
// This way, clicks and double-clicks are still available outside the tooltip.
3952+
set_flag(Window::FLAG_POPUP, false);
39183953
set_flag(Window::FLAG_NO_FOCUS, true);
39193954
popup(r);
39203955
}
@@ -3923,13 +3958,15 @@ EditorHelpBitTooltip::EditorHelpBitTooltip(Control *p_target) {
39233958
set_theme_type_variation("TooltipPanel");
39243959

39253960
timer = memnew(Timer);
3926-
timer->set_wait_time(0.2);
3927-
timer->connect("timeout", callable_mp(this, &EditorHelpBitTooltip::_safe_queue_free));
3961+
timer->set_wait_time(0.25);
3962+
timer->connect("timeout", callable_mp(static_cast<Node *>(this), &Node::queue_free));
39283963
add_child(timer);
39293964

39303965
ERR_FAIL_NULL(p_target);
39313966
p_target->connect(SceneStringName(mouse_exited), callable_mp(this, &EditorHelpBitTooltip::_start_timer));
39323967
p_target->connect(SceneStringName(gui_input), callable_mp(this, &EditorHelpBitTooltip::_target_gui_input));
3968+
3969+
set_process_internal(true);
39333970
}
39343971

39353972
#if defined(MODULE_GDSCRIPT_ENABLED) || defined(MODULE_MONO_ENABLED)

editor/editor_help.h

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -309,36 +309,36 @@ class EditorHelpBit : public VBoxContainer {
309309
void _notification(int p_what);
310310

311311
public:
312-
void parse_symbol(const String &p_symbol);
312+
void parse_symbol(const String &p_symbol, const String &p_prologue = String());
313313
void set_custom_text(const String &p_type, const String &p_name, const String &p_description);
314-
void set_description(const String &p_text);
315-
_FORCE_INLINE_ String get_description() const { return help_data.description; }
316314

317315
void set_content_height_limits(float p_min, float p_max);
318316
void update_content_height();
319317

320-
EditorHelpBit(const String &p_symbol = String());
318+
EditorHelpBit(const String &p_symbol = String(), const String &p_prologue = String(), bool p_allow_selection = true);
321319
};
322320

323321
// Standard tooltips do not allow you to hover over them.
324322
// This class is intended as a temporary workaround.
325323
class EditorHelpBitTooltip : public PopupPanel {
326324
GDCLASS(EditorHelpBitTooltip, PopupPanel);
327325

326+
static bool _is_tooltip_visible;
327+
328328
Timer *timer = nullptr;
329-
int _pushing_input = 0;
330-
bool _need_free = false;
329+
uint64_t _enter_tree_time = 0;
330+
bool _is_mouse_inside_tooltip = false;
331+
332+
static Control *_make_invisible_control();
331333

332334
void _start_timer();
333-
void _safe_queue_free();
334335
void _target_gui_input(const Ref<InputEvent> &p_event);
335336

336337
protected:
337338
void _notification(int p_what);
338-
virtual void _input_from_window(const Ref<InputEvent> &p_event) override;
339339

340340
public:
341-
static void show_tooltip(EditorHelpBit *p_help_bit, Control *p_target);
341+
static Control *show_tooltip(Control *p_target, const String &p_symbol, const String &p_prologue = String());
342342

343343
void popup_under_cursor();
344344

editor/editor_inspector.cpp

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,36 +1005,33 @@ void EditorProperty::_update_flags() {
10051005
}
10061006

10071007
Control *EditorProperty::make_custom_tooltip(const String &p_text) const {
1008-
String custom_warning;
1008+
String symbol;
1009+
String prologue;
1010+
10091011
if (object->has_method("_get_property_warning")) {
1010-
custom_warning = object->call("_get_property_warning", property);
1012+
const String custom_warning = object->call("_get_property_warning", property);
1013+
if (!custom_warning.is_empty()) {
1014+
prologue = "[b][color=" + get_theme_color(SNAME("warning_color")).to_html(false) + "]" + custom_warning + "[/color][/b]";
1015+
}
10111016
}
10121017

1013-
if (has_doc_tooltip || !custom_warning.is_empty()) {
1014-
EditorHelpBit *help_bit = memnew(EditorHelpBit);
1018+
if (has_doc_tooltip) {
1019+
symbol = p_text;
10151020

1016-
if (has_doc_tooltip) {
1017-
help_bit->parse_symbol(p_text);
1018-
1019-
const EditorInspector *inspector = get_parent_inspector();
1020-
if (inspector) {
1021-
const String custom_description = inspector->get_custom_property_description(p_text);
1022-
if (!custom_description.is_empty()) {
1023-
help_bit->set_description(custom_description);
1021+
const EditorInspector *inspector = get_parent_inspector();
1022+
if (inspector) {
1023+
const String custom_description = inspector->get_custom_property_description(p_text);
1024+
if (!custom_description.is_empty()) {
1025+
if (!prologue.is_empty()) {
1026+
prologue += '\n';
10241027
}
1028+
prologue += custom_description;
10251029
}
10261030
}
1031+
}
10271032

1028-
if (!custom_warning.is_empty()) {
1029-
String description = "[b][color=" + get_theme_color(SNAME("warning_color")).to_html(false) + "]" + custom_warning + "[/color][/b]";
1030-
if (!help_bit->get_description().is_empty()) {
1031-
description += "\n" + help_bit->get_description();
1032-
}
1033-
help_bit->set_description(description);
1034-
}
1035-
1036-
EditorHelpBitTooltip::show_tooltip(help_bit, const_cast<EditorProperty *>(this));
1037-
return memnew(Control); // Make the standard tooltip invisible.
1033+
if (!symbol.is_empty() || !prologue.is_empty()) {
1034+
return EditorHelpBitTooltip::show_tooltip(const_cast<EditorProperty *>(this), symbol, prologue);
10381035
}
10391036

10401037
return nullptr;
@@ -1331,9 +1328,7 @@ Control *EditorInspectorCategory::make_custom_tooltip(const String &p_text) cons
13311328
return nullptr;
13321329
}
13331330

1334-
EditorHelpBit *help_bit = memnew(EditorHelpBit(p_text));
1335-
EditorHelpBitTooltip::show_tooltip(help_bit, const_cast<EditorInspectorCategory *>(this));
1336-
return memnew(Control); // Make the standard tooltip invisible.
1331+
return EditorHelpBitTooltip::show_tooltip(const_cast<EditorInspectorCategory *>(this), p_text);
13371332
}
13381333

13391334
void EditorInspectorCategory::set_as_favorite(EditorInspector *p_for_inspector) {

editor/plugins/theme_editor_plugin.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2284,9 +2284,7 @@ ThemeTypeDialog::ThemeTypeDialog() {
22842284
///////////////////////
22852285

22862286
Control *ThemeItemLabel::make_custom_tooltip(const String &p_text) const {
2287-
EditorHelpBit *help_bit = memnew(EditorHelpBit(p_text));
2288-
EditorHelpBitTooltip::show_tooltip(help_bit, const_cast<ThemeItemLabel *>(this));
2289-
return memnew(Control); // Make the standard tooltip invisible.
2287+
return EditorHelpBitTooltip::show_tooltip(const_cast<ThemeItemLabel *>(this), p_text);
22902288
}
22912289

22922290
VBoxContainer *ThemeTypeEditor::_create_item_list(Theme::DataType p_data_type) {

0 commit comments

Comments
 (0)