Skip to content

Commit c032ce4

Browse files
committedJan 22, 2025
Merge pull request godotengine#101613 from kitbdev/fix-te-hover-mouse-exit
Fix TextEdit breakpoint hover not hiding on mouse exit
2 parents d4dafee + 8274e64 commit c032ce4

File tree

4 files changed

+140
-50
lines changed

4 files changed

+140
-50
lines changed
 

‎scene/gui/code_edit.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,6 @@ void CodeEdit::_notification(int p_what) {
275275
} break;
276276

277277
case NOTIFICATION_MOUSE_EXIT: {
278-
queue_redraw();
279278
symbol_tooltip_timer->stop();
280279
} break;
281280
}

‎scene/gui/text_edit.cpp

+52-49
Original file line numberDiff line numberDiff line change
@@ -1675,6 +1675,10 @@ void TextEdit::_notification(int p_what) {
16751675
drag_caret_force_displayed = false;
16761676
queue_redraw();
16771677
}
1678+
if (hovered_gutter != Vector2i(-1, -1)) {
1679+
hovered_gutter = Vector2i(-1, -1);
1680+
queue_redraw();
1681+
}
16781682
} break;
16791683
}
16801684
}
@@ -1843,18 +1847,18 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
18431847
int col = pos.x;
18441848

18451849
// Gutters.
1850+
Vector2i current_hovered_gutter = _get_hovered_gutter(mpos);
1851+
if (current_hovered_gutter != hovered_gutter) {
1852+
hovered_gutter = current_hovered_gutter;
1853+
queue_redraw();
1854+
}
1855+
if (hovered_gutter != Vector2i(-1, -1)) {
1856+
emit_signal(SNAME("gutter_clicked"), hovered_gutter.y, hovered_gutter.x);
1857+
return;
1858+
}
18461859
int left_margin = theme_cache.style_normal->get_margin(SIDE_LEFT);
1847-
for (int i = 0; i < gutters.size(); i++) {
1848-
if (!gutters[i].draw || gutters[i].width <= 0) {
1849-
continue;
1850-
}
1851-
1852-
if (mpos.x >= left_margin && mpos.x <= left_margin + gutters[i].width) {
1853-
emit_signal(SNAME("gutter_clicked"), line, i);
1854-
return;
1855-
}
1856-
1857-
left_margin += gutters[i].width;
1860+
if (mpos.x < left_margin + gutters_width + gutter_padding) {
1861+
return;
18581862
}
18591863

18601864
// Minimap.
@@ -2066,27 +2070,8 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
20662070
}
20672071
}
20682072

2069-
// Check if user is hovering a different gutter, and update if yes.
2070-
Vector2i current_hovered_gutter = Vector2i(-1, -1);
2071-
2072-
int left_margin = theme_cache.style_normal->get_margin(SIDE_LEFT);
2073-
if (mpos.x <= left_margin + gutters_width + gutter_padding) {
2074-
int hovered_row = get_line_column_at_pos(mpos).y;
2075-
for (int i = 0; i < gutters.size(); i++) {
2076-
if (!gutters[i].draw || gutters[i].width <= 0) {
2077-
continue;
2078-
}
2079-
2080-
if (mpos.x >= left_margin && mpos.x < left_margin + gutters[i].width) {
2081-
// We are in this gutter i's horizontal area.
2082-
current_hovered_gutter = Vector2i(i, hovered_row);
2083-
break;
2084-
}
2085-
2086-
left_margin += gutters[i].width;
2087-
}
2088-
}
2089-
2073+
// Update hovered gutter.
2074+
Vector2i current_hovered_gutter = _get_hovered_gutter(mpos);
20902075
if (current_hovered_gutter != hovered_gutter) {
20912076
hovered_gutter = current_hovered_gutter;
20922077
queue_redraw();
@@ -3115,24 +3100,16 @@ void TextEdit::drop_data(const Point2 &p_point, const Variant &p_data) {
31153100
}
31163101

31173102
Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const {
3118-
Point2i pos = get_line_column_at_pos(p_pos);
3119-
int row = pos.y;
3120-
3121-
int left_margin = theme_cache.style_normal->get_margin(SIDE_LEFT);
3122-
int gutter = left_margin + gutters_width;
3123-
if (p_pos.x < gutter) {
3124-
for (int i = 0; i < gutters.size(); i++) {
3125-
if (!gutters[i].draw) {
3126-
continue;
3127-
}
3128-
3129-
if (p_pos.x >= left_margin && p_pos.x < left_margin + gutters[i].width) {
3130-
if (gutters[i].clickable || is_line_gutter_clickable(row, i)) {
3131-
return CURSOR_POINTING_HAND;
3132-
}
3133-
}
3134-
left_margin += gutters[i].width;
3103+
Vector2i current_hovered_gutter = _get_hovered_gutter(p_pos);
3104+
if (current_hovered_gutter != Vector2i(-1, -1)) {
3105+
if (gutters[current_hovered_gutter.x].clickable || is_line_gutter_clickable(current_hovered_gutter.y, current_hovered_gutter.x)) {
3106+
return CURSOR_POINTING_HAND;
3107+
} else {
3108+
return CURSOR_ARROW;
31353109
}
3110+
}
3111+
int left_margin = theme_cache.style_normal->get_margin(SIDE_LEFT);
3112+
if (p_pos.x < left_margin + gutters_width + gutter_padding) {
31363113
return CURSOR_ARROW;
31373114
}
31383115

@@ -8321,9 +8298,35 @@ void TextEdit::_update_gutter_width() {
83218298
if (gutters_width > 0) {
83228299
gutter_padding = 2;
83238300
}
8301+
if (get_viewport()) {
8302+
hovered_gutter = _get_hovered_gutter(get_local_mouse_position());
8303+
}
83248304
queue_redraw();
83258305
}
83268306

8307+
Vector2i TextEdit::_get_hovered_gutter(const Point2 &p_mouse_pos) const {
8308+
int left_margin = theme_cache.style_normal->get_margin(SIDE_LEFT);
8309+
if (p_mouse_pos.x > left_margin + gutters_width + gutter_padding) {
8310+
return Vector2i(-1, -1);
8311+
}
8312+
int hovered_row = get_line_column_at_pos(p_mouse_pos, false).y;
8313+
if (hovered_row == -1) {
8314+
return Vector2i(-1, -1);
8315+
}
8316+
for (int i = 0; i < gutters.size(); i++) {
8317+
if (!gutters[i].draw || gutters[i].width <= 0) {
8318+
continue;
8319+
}
8320+
8321+
if (p_mouse_pos.x >= left_margin && p_mouse_pos.x < left_margin + gutters[i].width) {
8322+
return Vector2i(i, hovered_row);
8323+
}
8324+
8325+
left_margin += gutters[i].width;
8326+
}
8327+
return Vector2i(-1, -1);
8328+
}
8329+
83278330
/* Syntax highlighting. */
83288331
Vector<Pair<int64_t, Color>> TextEdit::_get_line_syntax_highlighting(int p_line) {
83298332
if (syntax_highlighter.is_null() || setting_text) {

‎scene/gui/text_edit.h

+1
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,7 @@ class TextEdit : public Control {
565565
Vector2i hovered_gutter = Vector2i(-1, -1); // X = gutter index, Y = row.
566566

567567
void _update_gutter_width();
568+
Vector2i _get_hovered_gutter(const Point2 &p_mouse_pos) const;
568569

569570
/* Syntax highlighting. */
570571
Ref<SyntaxHighlighter> syntax_highlighter;

‎tests/scene/test_text_edit.h

+87
Original file line numberDiff line numberDiff line change
@@ -8151,6 +8151,93 @@ TEST_CASE("[SceneTree][TextEdit] gutters") {
81518151
// Merging tested via CodeEdit gutters.
81528152
}
81538153

8154+
SUBCASE("[TextEdit] gutter mouse") {
8155+
DisplayServerMock *DS = (DisplayServerMock *)(DisplayServer::get_singleton());
8156+
// Set size for mouse input.
8157+
text_edit->set_size(Size2(200, 200));
8158+
8159+
text_edit->set_text("test1\ntest2\ntest3\ntest4");
8160+
text_edit->grab_focus();
8161+
8162+
text_edit->add_gutter();
8163+
text_edit->set_gutter_name(0, "test_gutter");
8164+
text_edit->set_gutter_width(0, 10);
8165+
text_edit->set_gutter_clickable(0, true);
8166+
8167+
text_edit->add_gutter();
8168+
text_edit->set_gutter_name(1, "test_gutter_not_clickable");
8169+
text_edit->set_gutter_width(1, 10);
8170+
text_edit->set_gutter_clickable(1, false);
8171+
8172+
text_edit->add_gutter();
8173+
CHECK(text_edit->get_gutter_count() == 3);
8174+
text_edit->set_gutter_name(2, "test_gutter_3");
8175+
text_edit->set_gutter_width(2, 10);
8176+
text_edit->set_gutter_clickable(2, true);
8177+
8178+
MessageQueue::get_singleton()->flush();
8179+
const int line_height = text_edit->get_line_height();
8180+
8181+
// Defaults to none.
8182+
CHECK(text_edit->get_hovered_gutter() == Vector2i(-1, -1));
8183+
CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_ARROW);
8184+
8185+
// Hover over gutter.
8186+
SEND_GUI_MOUSE_MOTION_EVENT(Point2(5, line_height + line_height / 2), MouseButtonMask::NONE, Key::NONE);
8187+
CHECK(text_edit->get_hovered_gutter() == Vector2i(0, 1));
8188+
SIGNAL_CHECK_FALSE("gutter_clicked");
8189+
CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_POINTING_HAND);
8190+
8191+
// Click on gutter.
8192+
SEND_GUI_MOUSE_BUTTON_EVENT(Point2(5, line_height / 2), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
8193+
CHECK(text_edit->get_hovered_gutter() == Vector2i(0, 0));
8194+
SIGNAL_CHECK("gutter_clicked", build_array(build_array(0, 0)));
8195+
8196+
// Click on gutter on another line.
8197+
SEND_GUI_MOUSE_BUTTON_EVENT(Point2(5, line_height * 3 + line_height / 2), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
8198+
CHECK(text_edit->get_hovered_gutter() == Vector2i(0, 3));
8199+
SIGNAL_CHECK("gutter_clicked", build_array(build_array(3, 0)));
8200+
8201+
// Unclickable gutter can be hovered.
8202+
SEND_GUI_MOUSE_MOTION_EVENT(Point2(15, line_height + line_height / 2), MouseButtonMask::NONE, Key::NONE);
8203+
CHECK(text_edit->get_hovered_gutter() == Vector2i(1, 1));
8204+
SIGNAL_CHECK_FALSE("gutter_clicked");
8205+
CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_ARROW);
8206+
8207+
// Unclickable gutter can be clicked.
8208+
SEND_GUI_MOUSE_BUTTON_EVENT(Point2(15, line_height * 2 + line_height / 2), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
8209+
CHECK(text_edit->get_hovered_gutter() == Vector2i(1, 2));
8210+
SIGNAL_CHECK("gutter_clicked", build_array(build_array(2, 1)));
8211+
CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_ARROW);
8212+
8213+
// Hover past last line.
8214+
SEND_GUI_MOUSE_MOTION_EVENT(Point2(5, line_height * 5), MouseButtonMask::NONE, Key::NONE);
8215+
CHECK(text_edit->get_hovered_gutter() == Vector2i(-1, -1));
8216+
SIGNAL_CHECK_FALSE("gutter_clicked");
8217+
CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_ARROW);
8218+
8219+
// Click on gutter past last line.
8220+
SEND_GUI_MOUSE_BUTTON_EVENT(Point2(5, line_height * 5), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
8221+
CHECK(text_edit->get_hovered_gutter() == Vector2i(-1, -1));
8222+
SIGNAL_CHECK_FALSE("gutter_clicked");
8223+
8224+
// Mouse exit resets hover.
8225+
SEND_GUI_MOUSE_MOTION_EVENT(Point2(5, line_height + line_height / 2), MouseButtonMask::NONE, Key::NONE);
8226+
CHECK(text_edit->get_hovered_gutter() == Vector2i(0, 1));
8227+
SEND_GUI_MOUSE_MOTION_EVENT(Point2(-1, -1), MouseButtonMask::NONE, Key::NONE);
8228+
CHECK(text_edit->get_hovered_gutter() == Vector2i(-1, -1));
8229+
8230+
// Removing gutter updates hover.
8231+
SEND_GUI_MOUSE_MOTION_EVENT(Point2(25, line_height + line_height / 2), MouseButtonMask::NONE, Key::NONE);
8232+
CHECK(text_edit->get_hovered_gutter() == Vector2i(2, 1));
8233+
text_edit->remove_gutter(2);
8234+
CHECK(text_edit->get_hovered_gutter() == Vector2i(-1, -1));
8235+
8236+
// Updating size updates hover.
8237+
text_edit->set_gutter_width(1, 20);
8238+
CHECK(text_edit->get_hovered_gutter() == Vector2i(1, 1));
8239+
}
8240+
81548241
SIGNAL_UNWATCH(text_edit, "gutter_clicked");
81558242
SIGNAL_UNWATCH(text_edit, "gutter_added");
81568243
SIGNAL_UNWATCH(text_edit, "gutter_removed");

0 commit comments

Comments
 (0)
Please sign in to comment.