Handling Mouse Hover on Overlapping Control Nodes

Godot Version

4.3

Question

I’m trying to make a card pop up when hovered over and return to its original position when the mouse exits the area. However, I ran into a problem: if two Control nodes overlap, the mouse_exited signal doesn’t emit when the cursor moves from one node to the other. This results in the card not returning to its original position. I’ve checked other forum posts about this issue, but still couldn’t solve it.

I found a note in the documentation that mentions a way to check if the mouse has left an area, ignoring top nodes:

Note: If you want to check whether the mouse truly left the area, ignoring any top nodes, you can use code like this:

func _on_mouse_exited():
	if not Rect2(Vector2(), size).has_point(get_local_mouse_position()):
		# Not hovering over area.

And I tried to use this snippet, here’s my code:

extends ColorRect

@onready var initial_position = position

func _on_mouse_entered() -> void:
		for tween in get_tree().get_processed_tweens():
			tween.kill()
		var tween = get_tree().create_tween()
		tween.tween_property(self, "position:y", initial_position.y - 30, 0.2).set_trans(Tween.TRANS_CUBIC)


func _on_mouse_exited() -> void:
		for tween in get_tree().get_processed_tweens():
			tween.kill()
		if not Rect2(Vector2(), size).has_point(get_local_mouse_position()):
			var tween = get_tree().create_tween()
			tween.tween_property(self, "position:y", initial_position.y, 0.2).set_trans(Tween.TRANS_CUBIC)

The issue is that the mouse_exited signal never emits when the mouse moves directly from one overlapping node to another. I believe there’s a simple solution to this, but I just can’t seem to figure it out. Any suggestions?

I really have no clue what’s happening, but I tried again from scratch this morning, don’t know what has changed, but now it works.

Here’s my code for ColorRect card:

extends ColorRect

var initial_position: Vector2

func _on_mouse_entered() -> void:
	var tween = get_tree().create_tween()
	tween.tween_property(self, "position:y", initial_position.y - 30.0, 0.1)


func _on_mouse_exited() -> void:
	var tween = get_tree().create_tween()
	tween.tween_property(self, "position:y", initial_position.y, 0.1)

And here’s the code for HBoxContainer that has ColorRect cards:

extends HBoxContainer

const HEIGHT_CURVE = preload("res://height_curve.tres")

func _ready() -> void:
	await get_tree().process_frame
	fan_cards()

func fan_cards():
	var card_count = len(get_children())
	var cards = get_children()
	for card_index in card_count:
		var card: ColorRect = cards[card_index] as ColorRect
		card.pivot_offset = card.size / 2
		var mapped_angle = remap(card.position.x + card.size.x / 2, 0, size.x, -15, 15)
		var mapped_height = HEIGHT_CURVE.sample(remap(card_index, 0, card_count - 1, 0, 1))
		card.position.y += mapped_height * 15.0
		card.color = Color(randf(), randf(), randf())
		card.set_rotation_degrees(mapped_angle)
		card.initial_position = card.position

The reason why i’m awaiting process_frame is because HBoxContainer controls position and rotation of their children, so there’s some sort of race condition where without the awaiting the container would override my changes.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.