Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is there a way to simulate mouse dragging in GUT 4? #608

Closed
kaiharuto opened this issue May 18, 2024 · 8 comments
Closed

Is there a way to simulate mouse dragging in GUT 4? #608

kaiharuto opened this issue May 18, 2024 · 8 comments

Comments

@kaiharuto
Copy link

I'm using Godot 4.2-stable and would like to test controls that use mouse dragging. Despite there being support for clicking and mouse movement, I wasn't able to reproduce this functionality using any of the provided methods for clicking, moving or waiting for a few frames to simulate a mouse button being held during movement. Is there a way to achieve this?

@kaiharuto kaiharuto changed the title Is there a way to simulate mouse drags in GUT 4? Is there a way to simulate mouse dragging in GUT 4? May 18, 2024
@bitwes
Copy link
Owner

bitwes commented May 18, 2024

There's a test script that accidentally made its way into main that does a drag. Let me know if that helps. It was originally part of some enhancements for InputSender, but those changes haven't been fully implemented yet.

https://github.com/bitwes/Gut/blob/main/test%2Fintegration%2Ftest_more_input_ideas.gd#L71

@TGRCdev
Copy link

TGRCdev commented May 20, 2024

I was able to simulate it with InputFactory. InputSender.mouse_relative_motion does not set the button_mask variable in InputEventMouseMotion, which is what Godot uses to track dragging.

Some quick pseudo-code of how to simulate click-and-drag:

var control_pos = Vector2i(50,50) # Where your draggable control is
var dest_pos = Vector2i(150,50) # Where you want to drop it
var sender = InputSender.new(Input)
Input.use_accumulated_input = false # Either use this or 'await sender.wait_frames(1).idle` after every event
var drag_ev = InputFactory.mouse_motion(dest_pos)
drag_ev.relative = (dest_pos - control_pos)
drag_ev.button_mask = MOUSE_BUTTON_MASK_LEFT
sender.mouse_left_button_down(control_pos)
sender.send_event(drag_ev)
sender.mouse_left_button_up(dest_pos)

Here's my unit test where I test that items in a player's inventory can be moved with click-and-drag:

drag_event.mp4

@bitwes
Copy link
Owner

bitwes commented May 22, 2024

InputSender.mouse_relative_motion does not set the button_mask variable in InputEventMouseMotion, which is what Godot uses to track dragging.

Can you paste your input handling code? I'm not sure what you mean by this.

@TGRCdev
Copy link

TGRCdev commented May 25, 2024

I'm relatively new to GUT, but I made a minimal project explaining what I tried to do, why it didn't work, and what I had to do to work around it: GUTInputHandling.zip

This project has a TextureRect with basic code to set the drag data and preview:

extends TextureRect

func _get_drag_data(_at_position):
	var preview = self.duplicate(0)
	set_drag_preview(preview)
	return self

func _gui_input(event):
	print(event)

and two unit tests, test_drag_using_input_sender and test_drag_using_input_factory:

extends GutTest

# _drag_node spawns in the top-left corner with the size 128x128
var _drag_node: Control
var _sender

func before_all():
	_drag_node = preload("res://DragTest.tscn").instantiate()
	add_child(_drag_node)
	_sender = InputSender.new(Input)
	print(_drag_node)
func after_each():
	_sender.clear()
func after_all():
	_drag_node.queue_free()

# This test tries to use exclusively InputSender to press, drag,
# and release the control.
func test_drag_using_input_sender():
	await (_sender.mouse_set_position(Vector2(64,64)) # Set position to middle of control
		.mouse_left_button_down().wait_secs(1) # Begin click
		.mouse_relative_motion(Vector2(128,0)) # Move to the right to begin drag
	).wait_secs(1).idle
	
	assert_not_null(get_viewport().gui_get_drag_data(), "Control is being dragged")
	
	await _sender.mouse_left_button_up().wait_frames(1).idle # Release the mouse
	
	assert_null(get_viewport().gui_get_drag_data(), "Control is no longer being dragged")

# This test uses InputSender to press and release the mouse button,
# but generates a drag event with InputFactory
func test_drag_using_input_factory():
	await _sender.mouse_left_button_down(Vector2(64,64)).wait_secs(1) # Mouse down on control
	
	var event = InputFactory.mouse_motion(Vector2(196,64)) # Create a mouse motion event at the destination
	event.relative = Vector2i(128,0) # Set the relative motion
	event.button_mask = MOUSE_BUTTON_MASK_LEFT # Indicate we are holding left click
	
	await _sender.send_event(event).wait_secs(1).idle # Drag event
	
	assert_not_null(get_viewport().gui_get_drag_data(), "Control is being dragged")
	
	await _sender.mouse_left_button_up(Vector2(196,64)).wait_frames(1).idle # Release the mouse
	
	assert_null(get_viewport().gui_get_drag_data(), "Control is no longer being dragged")

In test_drag_using_input_sender, the test tries to perform a drag exclusively through InputSender's relative events. It does not end up working, and the console shows that the InputEventMouseMotion event has the button_index set to 0, which means the left mouse button isn't held down.

input_sender.mp4
InputEventMouseButton: button_index=1, mods=none, pressed=true, canceled=false, position=((64, 64)), button_mask=0, double_click=false
InputEventMouseMotion: button_mask=0, position=((192, 64)), relative=((128, 0)), velocity=((0, 0)), pressure=0.00, tilt=((0, 0)), pen_inverted=(false)
InputEventMouseButton: button_index=1, mods=none, pressed=false, canceled=false, position=((192, 64)), button_mask=0, double_click=false

In test_drag_using_input_factory, I create a InputEventMouseMotion event manually, set the relative to simulate the mouse dragging to the right, and set button_index to indicate that the left mouse button is down. This results in the drag event occurring, as seen by the preview node appearing and the test passing.

input_factory.mp4

It could also be possible that I'm using InputSender incorrectly. Like I said, I'm basically brand new to GUT.

@bitwes
Copy link
Owner

bitwes commented May 29, 2024

Thank you, I did not know about the drag related methods on Controls. My whole approach has been to handle drags manually with input events. Here's a simple test drag button I use in tests.

class DraggableButton:
	extends Button

	var _mouse_down = false

	func _gui_input(event):
		if(event is InputEventMouseButton):
			_mouse_down = event.pressed
		elif(event is InputEventMouseMotion and _mouse_down):
			position += event.relative

I think the mouse motion methods in InputSender can be changed to send the button index. The button is already being tracked when you call things like mouse_left_button_down, so sending that along with all the mouse motion events shouldn't be an issue.

@bitwes
Copy link
Owner

bitwes commented May 29, 2024

@TGRCdev created an issue and implemented the feature. This will be in the next release. See #612, #613.

@bitwes
Copy link
Owner

bitwes commented May 31, 2024

@kaiharuto closing this out since examples have been given and it is gone off topic. If you have any more questions feel free to reopen or make another issue.

@bitwes bitwes closed this as completed May 31, 2024
@kaiharuto
Copy link
Author

@kaiharuto closing this out since examples have been given and it is gone off topic. If you have any more questions feel free to reopen or make another issue.

Sorry for being inactive for so long. Thank you all for the detailed thread, can't wait to try out the solutions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants