TextureRect made to Drag its Parent Panel

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Whitehorn

Hello, I’m trying to make a panel with one small texture rect (DragButton) which, when clicked and dragged, will drag and reposition its parent node (QueryPanel) and all of QueryPanel’s children.

Right now, the code below will drag and resposition the DragButton. I cannot figure out how to make it drag and reposition QueryPanel.

Any ideas?

 extends TextureRect

var dragPosition = null

func _ready():
	hint_tooltip = "Drag Panel" 

func _on_DragButton_gui_input(event):
	if event is InputEventMouseButton:
		if (event.is_pressed() and event.button_index == BUTTON_LEFT):
			# start dragging
			dragPosition = get_global_mouse_position() - rect_global_position
		else: 
			# end dragging
			dragPosition = null
			

	if event is InputEventMouseMotion and dragPosition: 
		rect_global_position = get_global_mouse_position() - dragPosition
	

For others using this, there’s still the issue of keeping this panel on screen.

I know that I have to use _process(delta) to somehow clamp the panel to the viewport size, but I can’t get it to work.

Any suggestions?

Whitehorn | 2019-12-16 18:18

:bust_in_silhouette: Reply From: Klagsam

You could try it with a signal.
At the top of the script you declare the signal: signal dragging(Vector2(position))
Then you emit the signal:

func _on_DragButton_gui_input(event):
if event is InputEventMouseButton:
    if (event.is_pressed() and event.button_index == BUTTON_LEFT):
        # start dragging
        dragPosition = get_global_mouse_position() - rect_global_position
        emit_signal("dragging",rect_global_position)
    else: 
        # end dragging
        dragPosition = null

May you will have to emit dragPositioninstead of rect_global position - that you have to test, I am always unsure with these.

However on your Parent Panel you can now connect the signal dragging. For that you have to click on your button and then Node where you will find your signal. Connect it and in the resulting function you could write:

func _on_dragging(position):
    rect_global_position = position

Another possibility to implement this is with get_parent()

On your button script you just decalte another variable:
var button_parent : Control = get_parent()

Then you could use button_parent.rect_global_position = rect_global_position

Personally I always try to avoid get_parent()because it can easily screw up entire scipts when you have to move scenes inside a node.

Hope that helps

Thanks for the detailed comment!

I am not familiar with using signals and Vector2ds… when I declare the signal, it’s saying there’s an expected “identifier” missing the argument. I only see the Vector2d arguement, is there something else missing?

signal dragging(Vector2(position))

Whitehorn | 2019-12-14 09:42

I am sorry. You have to declare signal dragging(position)
With that you tell the engine there will be a signal called dragging with one argument position.
Since the last update you could probably also declare signal dragging(position : Vector2) to clarify the type of the argument.

positionis then given to the function the signal connects to. i.e. to func _on_dragging(position)

I would heavily recommend to read up on signals. They are incredibly useful. I use them all they time to communicate between scenes.

Klagsam | 2019-12-14 11:15

Thanks!

if event is InputEventMouseMotion and dragPosition: 
	rect_global_position = get_global_mouse_position() - dragPosition
	emit_signal("dragging", rect_global_position)

This is the part that worked the best for me. If I had it in the previous if statement, it would only update the panel when I left clicked. With if event is InputEventMouseMotion and dragPosition: the panel updates when I stop dragging.

You are a gentleman and a scholar!

Whitehorn | 2019-12-14 11:29