Mouse enter events not triggering until other input event completed

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By JymWythawhy
:warning: Old Version Published before Godot 3 was released.

I have a grid of tiles that change sprite textures when clicked on (Black or white for marked or not marked). I am trying to get it to the point where if you then drag the clicked mouse over other tiles, they will also change. The code is like this:

export (Texture) var Blank
export (Texture) var Filled

var filled = false

func _on_Tile_input_event( ev ):
    if(ev.type==InputEvent.Mouse_Button and ev.pressed):
        if (ev.button_index==BUTTON_LEFT):
            if(filled)
                get_node("Sprite").set_texture(Blank)
                filled = false
            else:
                get_node("Sprite").set_texture(Filled)
                filled = true

func _on_Tile_mouse_enter():
    if Input.is_mouse_button_pressed(BUTTON_LEFT):
        if(filled)
            get_node("Sprite").set_texture(Blank)
            filled = false
        else:
            get_node("Sprite").set_texture(Filled)
            filled = true

If I click outside of any tiles and then drag onto tiles, they will fill in as the mouse goes over them, just like I expect. If I click on a single tile, that one will change, just as I expect. However, if I click on a tile and then drag, only that first tile will change, and the _on_Tile_mouse_enter function will not be called. Its like the _on_Tile_input_event function has captured the event system and won’t allow anything else through until I release the mouse button. Is there a way to tell it it can go on its way after registering the mouse down, and no need to wait around for the mouse up?

You should put a print() statement at the front of these enter events to confirm that.

avencherus | 2016-11-21 21:42

Great idea- When I do so, I see what I described above as well. If I am not clicking on a tile first, every time I mouse over a tile I see my print statement, as expected. But if I have clicked on a tile first and drag away from it, no print statements are made for the mouse overs. Its like it can’t handle other mouse inputs until I let up the mouse.

JymWythawhy | 2016-11-22 01:14

What’s that code look like? I haven’t been able to reproduce what you’re describing.

avencherus | 2016-11-22 01:19

Here is a minimal example I created that exhibits the problem. GitHub - JymWythawhy/MinimalExample: A minimal example of a problem I encountered in Godot

JymWythawhy | 2016-11-22 02:32

Took a look. That does seem unusual. There was a brief discussion here, but I didn’t really see anything else: mouse_enter signal doesn't fire if lmb is held down · Issue #6362 · godotengine/godot · GitHub

avencherus | 2016-11-22 03:05

:bust_in_silhouette: Reply From: avencherus

I see, it may be a designed that way for drag and drop reasons.

If you want to circumvent that behavior I have two suggestions. The first is a bit hacky and may create other undesired effects, so it may need other flags. It would be to code a mouse button release event.

func _on_Tile_input_event( ev ):
	if(ev.type==InputEvent.MOUSE_BUTTON and ev.pressed):
		if (ev.button_index==BUTTON_LEFT):
			if(filled):
				get_node("Sprite").set_texture(Blank)
				filled = false
			else:
				get_node("Sprite").set_texture(Filled)
				filled = true
			
			# Release button
			var event = InputEvent()
			event.type = InputEvent.MOUSE_BUTTON
			event.button_index=BUTTON_LEFT
			event.pressed = false
			get_tree().input_event(event)

The second solution, which may be more robust, but will take a little extra effort to implement, is to create your own mouse over detection. You would poll the mouse coordinates, and use rectangle collision detection to know if the cursor is in bounds of any given tile.

Many nodes can return their rectangle as Rect2, and inside that is an collision method where you can send the mouse coordinates as a Vector2.

There might be some other option for input handling that escapes me, but it may be because it’s obscure.

Thanks! I will try both of these.

JymWythawhy | 2016-11-22 13:20

You’re welcome. :slight_smile:

avencherus | 2016-11-22 22:07

Ran into the same problem and the has point method worked for me inside the button up event:

get_rect().has_point(get_global_mouse_position())

Inobar | 2020-01-23 01:14

get_rect() didn’t quite work for me. Since it checks as if the rect always starts in the top-left corner of the screen.
I used this get_global_rect() instead:

get_global_rect().has_point(get_global_mouse_position())

Thanks a lot for the help though guys.

VastView | 2020-07-06 09:51

:bust_in_silhouette: Reply From: ntropy.space

I’ve also struggled with this issue and I found that you can apply drop event on any Control node. This way mouse_entered event will continue firing while starting pressed event on same node.

func get_drag_data(position):
  print("start drag")
  return true

func can_drop_data(pos, data):
  print("dragging")
  return true

func drop_data(pos, data):
  print("drop")

Tested in Godot 3.1.1


Edit:
There is better dnd solution using the _input event, and its good to be used as a gui_input signal from a Control node.

var dragging = false

func _input(event):
  if event is InputEventMouseButton:
    if dragging:
      dragging = false
      print("drop")
    else:
      dragging = true
      print("start drag")
      
  elif event is InputEventMouseMotion:
    if dragging:
      print("dragging")