Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
a0bb146
Add mouse event hit testing and signals to 2D nodes
Jan 6, 2026
7b46d22
Merge branch 'godotengine:master' into master
Jan 6, 2026
252ab83
i was being dumb, i forgot printf is not the same as gd::print
Jan 7, 2026
b700fcd
Merge branch 'godotengine:master' into master
Jan 7, 2026
8ede504
Merge branch 'godotengine:master' into master
Jan 8, 2026
40026c6
Merge branch 'godotengine:master' into master
Jan 9, 2026
96fd2e6
Refactor CanvasItem input handling and mouse tracking
Jan 9, 2026
fad37b6
Skip input processing when CanvasItem is not visible
Jan 9, 2026
0082063
Fixed Styling and removed random whitespaces i've forgot over Line2D …
Jan 9, 2026
7a1c493
More whitespaces gone unnoticed
Jan 9, 2026
d8c0f16
... whitespaces
Jan 9, 2026
e9ed590
whitespace missing before a comment...
Jan 9, 2026
e63f035
Update canvas_item.cpp
Jan 9, 2026
21c532e
Update sprite_2d.cpp
Jan 9, 2026
3ef7814
styling compliance... whitespaces
Jan 9, 2026
7e33d3e
Update sprite_2d.h
Jan 9, 2026
6d9f697
Add mouse interaction signals to Polygon2D and Sprite2D docs. Hopeful…
Jan 9, 2026
afa77c6
Clarify input processing requirement in signal docs
Jan 9, 2026
097f2db
Update Sprite2D.xml
Jan 9, 2026
41a1d8b
Refactor mouse picking and signals in CanvasItem
Jan 14, 2026
869c3f4
Update polygon_2d.h
Jan 14, 2026
0fd6881
Update canvas_item.cpp
Jan 14, 2026
6bd465a
Update scene_tree.cpp
Jan 14, 2026
d96121e
Small fix
Jan 14, 2026
ffeadcb
Update canvas_item.cpp
Jan 14, 2026
70598f3
Update canvas_item.h
Jan 14, 2026
ceeddd3
Update polygon_2d.cpp
Jan 14, 2026
f840c82
Update polygon_2d.h
Jan 14, 2026
c5d7d1a
Update sprite_2d.h
Jan 14, 2026
eadbea0
Update sprite_2d.cpp
Jan 14, 2026
942aaf5
Update control.h
Jan 14, 2026
f57a01c
fixed documentation and spacing
Jan 14, 2026
f3c9047
Update CanvasItem.xml
Jan 14, 2026
e2a0b96
Update CanvasItem.xml
Jan 14, 2026
c16da19
Merge branch 'godotengine:master' into MNP
Jan 14, 2026
7ecb76d
Merge pull request #1 from Strauji/MNP
Jan 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions doc/classes/CanvasItem.xml
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,14 @@
Usually, this is the same as this node's viewport (see [method Node.get_viewport] and [method Viewport.find_world_2d]).
</description>
</method>
<method name="has_point" qualifiers="const">
<return type="bool" />
<param index="0" name="point" type="Vector2" />
<description>
Returns [code]true[/code] if the given [param point] (in local coordinates) is inside the visible area of this [CanvasItem]. The visible area is only calculated in [Sprite2D] and [Polygon2D].
This method is used internally for mouse picking when mouse picking is enabled.
</description>
</method>
<method name="hide">
<return type="void" />
<description>
Expand All @@ -568,6 +576,12 @@
Returns [code]true[/code] if the node receives [constant NOTIFICATION_LOCAL_TRANSFORM_CHANGED] whenever its local transform changes. This is enabled with [method set_notify_local_transform].
</description>
</method>
<method name="is_mouse_picking_enabled" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if mouse picking is enabled for this [CanvasItem].
</description>
</method>
<method name="is_transform_notification_enabled" qualifiers="const">
<return type="bool" />
<description>
Expand Down Expand Up @@ -622,6 +636,13 @@
[b]Note:[/b] [param name] is case-sensitive and must match the name of the uniform in the code exactly (not the capitalized name in the inspector).
</description>
</method>
<method name="set_mouse_picking_enabled">
<return type="void" />
<param index="0" name="enabled" type="bool" />
<description>
Enables or disables mouse picking for this [CanvasItem].
</description>
</method>
<method name="set_notify_local_transform">
<return type="void" />
<param index="0" name="enable" type="bool" />
Expand Down Expand Up @@ -727,6 +748,22 @@
Emitted when the [CanvasItem]'s boundaries (position or size) change, or when an action took place that may have affected these boundaries (e.g. changing [member Sprite2D.texture]).
</description>
</signal>
<signal name="mouse_entered_node">
<description>
Emitted when the mouse cursor enters the CanvasItem boundaries. Requires input processing enabled to be emitted.
</description>
</signal>
<signal name="mouse_exited_node">
<description>
Emitted when the mouse cursor exits the CanvasItem boundaries. Requires input processing enabled to be emitted.
</description>
</signal>
<signal name="mouse_pressed_node">
<param index="0" name="button_index" type="int" />
<description>
Emitted when a mouse button is pressed while the cursor is over the CanvasItem boundaries. The [param button_index] indicates which button was pressed. Requires input processing enabled to be emitted.
</description>
</signal>
<signal name="visibility_changed">
<description>
Emitted when the [CanvasItem]'s visibility changes, either because its own [member visible] property changed or because its visibility in the tree changed (see [method is_visible_in_tree]).
Expand Down
5 changes: 5 additions & 0 deletions scene/2d/polygon_2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ void Polygon2D::_skeleton_bone_setup_changed() {
queue_redraw();
}

bool Polygon2D::has_point(const Vector2 &p_point) const {
Vector2 local = to_local(p_point);
return Geometry2D::is_point_in_polygon(local, polygon);
}

void Polygon2D::_notification(int p_what) {
if (p_what == NOTIFICATION_TRANSFORM_CHANGED && !Engine::get_singleton()->is_editor_hint()) {
return; // Mesh recreation for NOTIFICATION_TRANSFORM_CHANGED is only needed in editor.
Expand Down
2 changes: 2 additions & 0 deletions scene/2d/polygon_2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ class Polygon2D : public Node2D {
void set_skeleton(const NodePath &p_skeleton);
NodePath get_skeleton() const;

bool has_point(const Vector2 &p_point) const override;

#ifndef NAVIGATION_2D_DISABLED
private:
static Callable _navmesh_source_geometry_parsing_callback;
Expand Down
16 changes: 15 additions & 1 deletion scene/2d/sprite_2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,21 @@ void Sprite2D::_validate_property(PropertyInfo &p_property) const {
}
}

bool Sprite2D::has_point(const Vector2 &p_point) const {
if (!texture.is_valid()) {
return false;
}
Vector2 local = to_local(p_point);
Rect2 rect = get_rect();
if (!rect.has_point(local)) {
return false;
}
Vector2 uv = (local - rect.position) / rect.size;
uv *= texture->get_size();
Color c = texture->get_image()->get_pixelv(uv);
return c.a > 0.01;
}

void Sprite2D::_texture_changed() {
// Changes to the texture need to trigger an update to make
// the editor redraw the sprite with the updated texture.
Expand Down Expand Up @@ -532,7 +547,6 @@ void Sprite2D::_bind_methods() {

ADD_SIGNAL(MethodInfo("frame_changed"));
ADD_SIGNAL(MethodInfo("texture_changed"));

ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
ADD_GROUP("Offset", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "centered"), "set_centered", "is_centered");
Expand Down
2 changes: 2 additions & 0 deletions scene/2d/sprite_2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,5 +135,7 @@ class Sprite2D : public Node2D {
Rect2 get_rect() const;
virtual Rect2 get_anchorable_rect() const override;

bool has_point(const Vector2 &p_point) const override;

Sprite2D();
};
2 changes: 1 addition & 1 deletion scene/gui/control.h
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,7 @@ class Control : public CanvasItem {
virtual void gui_input(const Ref<InputEvent> &p_event);
void accept_event();

virtual bool has_point(const Point2 &p_point) const;
bool has_point(const Point2 &p_point) const override;

void set_mouse_filter(MouseFilter p_filter);
MouseFilter get_mouse_filter() const;
Expand Down
29 changes: 29 additions & 0 deletions scene/main/canvas_item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1122,6 +1122,19 @@ void CanvasItem::set_canvas_item_use_identity_transform(bool p_enable) {
}
}

bool CanvasItem::has_point(const Vector2 &p_point) const { //We'll override this in Sprite2D and Polygon2D
return false;
}

void CanvasItem::input(const Ref<InputEvent> &p_event) { //It needs to be enabled by setting set_process_input(true);
Ref<InputEventMouse> mouse_event = p_event;
Ref<InputEventMouseButton> mouse_button_event = mouse_event;
if (mouse_button_event.is_valid() && mouse_button_event->is_pressed()) { //If the mouse is over the item and a button is pressed, emit signal
emit_signal("mouse_pressed_node", mouse_button_event->get_button_index());
}
get_viewport()->set_input_as_handled(); //We mark the event as handled to avoid propagating it to other nodes
}

Rect2 CanvasItem::get_viewport_rect() const {
ERR_READ_THREAD_GUARD_V(Rect2());
ERR_FAIL_COND_V(!is_inside_tree(), Rect2());
Expand Down Expand Up @@ -1278,6 +1291,14 @@ Vector2 CanvasItem::get_local_mouse_position() const {
return get_global_transform().affine_inverse().xform(get_global_mouse_position());
}

void CanvasItem::set_mouse_picking_enabled(bool p_enabled) {
mouse_picking = p_enabled;
}

bool CanvasItem::is_mouse_picking_enabled() const {
return mouse_picking;
}

void CanvasItem::force_update_transform() {
ERR_THREAD_GUARD;
ERR_FAIL_COND(!is_inside_tree());
Expand Down Expand Up @@ -1465,6 +1486,11 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_clip_children_mode", "mode"), &CanvasItem::set_clip_children_mode);
ClassDB::bind_method(D_METHOD("get_clip_children_mode"), &CanvasItem::get_clip_children_mode);

ClassDB::bind_method(D_METHOD("is_mouse_picking_enabled"), &CanvasItem::is_mouse_picking_enabled);
ClassDB::bind_method(D_METHOD("set_mouse_picking_enabled", "enabled"), &CanvasItem::set_mouse_picking_enabled);

ClassDB::bind_method(D_METHOD("has_point", "point"), &CanvasItem::has_point);

GDVIRTUAL_BIND(_draw);

ADD_GROUP("Visibility", "");
Expand Down Expand Up @@ -1498,6 +1524,9 @@ void CanvasItem::_bind_methods() {
ADD_SIGNAL(MethodInfo("visibility_changed"));
ADD_SIGNAL(MethodInfo("hidden"));
ADD_SIGNAL(MethodInfo("item_rect_changed"));
ADD_SIGNAL(MethodInfo("mouse_entered_node"));
ADD_SIGNAL(MethodInfo("mouse_exited_node"));
ADD_SIGNAL(MethodInfo("mouse_pressed_node", PropertyInfo(Variant::INT, "button_index")));

BIND_CONSTANT(NOTIFICATION_TRANSFORM_CHANGED);
BIND_CONSTANT(NOTIFICATION_LOCAL_TRANSFORM_CHANGED);
Expand Down
8 changes: 7 additions & 1 deletion scene/main/canvas_item.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ class CanvasItem : public Node {
bool notify_local_transform = false;
bool notify_transform = false;
bool hide_clip_children = false;
bool mouse_inside = false;
bool mouse_picking = false;

ClipChildrenMode clip_children_mode = CLIP_CHILDREN_DISABLED;

Expand Down Expand Up @@ -180,7 +182,7 @@ class CanvasItem : public Node {
void item_rect_changed(bool p_size_changed = true);

void set_canvas_item_use_identity_transform(bool p_enable);

virtual void input(const Ref<InputEvent> &p_event) override;
void _notification(int p_what);
static void _bind_methods();

Expand Down Expand Up @@ -393,6 +395,9 @@ class CanvasItem : public Node {
Vector2 get_global_mouse_position() const;
Vector2 get_local_mouse_position() const;

void set_mouse_picking_enabled(bool p_enable);
bool is_mouse_picking_enabled() const;

void set_notify_local_transform(bool p_enable);
bool is_local_transform_notification_enabled() const;

Expand All @@ -417,6 +422,7 @@ class CanvasItem : public Node {
CanvasLayer *get_canvas_layer_node() const;

virtual PackedStringArray get_configuration_warnings() const override;
bool virtual has_point(const Vector2 &p_point) const;

CanvasItem();
~CanvasItem();
Expand Down
45 changes: 43 additions & 2 deletions scene/main/scene_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1447,9 +1447,32 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal
}

switch (p_call_type) {
case CALL_INPUT_TYPE_INPUT:
n->_call_input(p_input);
case CALL_INPUT_TYPE_INPUT: {
CanvasItem *ci = Object::cast_to<CanvasItem>(n);
if (ci) {
if (!ci->is_mouse_picking_enabled()) {
continue;
}
if (!ci->is_visible_in_tree()) {
continue;
}
Ref<InputEventMouse> mouse_event = p_input;
if (mouse_event.is_valid()) {
if (ci->has_point(mouse_event->get_position())) {
_change_mouse_over_node(ci);
n->_call_input(p_input);
break;
}
}
} else {
n->_call_input(p_input);
break;
}
if (i == 0) {
_change_mouse_over_node(nullptr);
}
break;
}
case CALL_INPUT_TYPE_SHORTCUT_INPUT: {
const Control *c = Object::cast_to<Control>(n);
if (c) {
Expand Down Expand Up @@ -1861,6 +1884,24 @@ bool SceneTree::is_multiplayer_poll_enabled() const {
return multiplayer_poll;
}

void SceneTree::_change_mouse_over_node(Node *p_node) {
if (mouse_over == p_node) { // No change
return;
}
if (p_node == nullptr) { //Mouse exited node
if (mouse_over) {
mouse_over->emit_signal(SNAME("mouse_exited_node"));
}
} else {
if (mouse_over) { //Mouse entered new node, so we exit the previous one
mouse_over->emit_signal(SNAME("mouse_exited_node"));
}
mouse_over = p_node;
p_node->emit_signal(SNAME("mouse_entered_node"));
}
mouse_over = p_node;
}

void SceneTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_root"), &SceneTree::get_root);
ClassDB::bind_method(D_METHOD("has_group", "name"), &SceneTree::has_group);
Expand Down
2 changes: 2 additions & 0 deletions scene/main/scene_tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ class SceneTree : public MainLoop {
Ref<Material> debug_paths_material;
Ref<Material> collision_material;
int collision_debug_contacts;
Node *mouse_over = nullptr;

void _flush_scene_change();

Expand Down Expand Up @@ -287,6 +288,7 @@ class SceneTree : public MainLoop {
protected:
void _notification(int p_notification);
static void _bind_methods();
void _change_mouse_over_node(Node *p_node);

public:
enum {
Expand Down