- Edited
Hello! I tried to write a as-simple-as-I-could script for draggable controls (in my case PanelContainers), and I figured someone else might find it useful:
If it's inside another Control node, it will stay within its boundaries, and move to the bottom of the tree when it's dragged. I think changing z-Index would be more efficient than reordering the tree, but I didn't wanna deal with that
If you only want a part of it to be draggable (in my example it would make sense only for the title bar to trigger drag) you could check for mouse position or have the drag be triggered by a child node, or add a child with "Mouse Filter: Stop" and cover the unwanted area with it. To do this with the PanelContainer I just changed _GuiInput to:
offset = GetLocalMousePosition();
if (new Rect2(Vector2.Zero, new Vector2(Size.X, 35)).HasPoint(offset))
{
isDragging = true;
}
where 35px is the top texture margin of the StyleBoxTexture.
Structure in the example above:
GDScript:
extends Control
var isDragging = false
var movedToTop = false
var parent
var offset
func _gui_input(event):
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
offset = get_local_mouse_position()
isDragging = true
func _ready():
var _parent = get_parent()
if _parent is Control:
parent = _parent
func _process(_delta):
if isDragging:
if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):
var pos = get_viewport().get_mouse_position() - offset;
if (parent != null):
pos -= parent.global_position
var _w = parent.size.x - size.x
var _h = parent.size.y - size.y
if (pos.x <= 0):
pos.x = 0
elif (pos.x > _w):
pos.x = _w
if (pos.y <= 0):
pos.y = 0
elif (pos.y > _h):
pos.y = _h
if !movedToTop && parent != null:
parent.move_child(self, parent.get_child_count())
movedToTop = true
position = pos
else:
isDragging = false
movedToTop = false
C#:
using Godot;
public partial class DraggableWindow : Control
{
bool isDragging = false;
bool movedToTop = false;
Control parent = null;
Vector2 offset;
public override void _GuiInput(InputEvent _event)
{
if (_event is InputEventMouseButton _mouseEvent)
{
if (_mouseEvent.ButtonIndex == MouseButton.Left &&_mouseEvent.Pressed)
{
offset = GetLocalMousePosition();
isDragging = true;
}
}
}
public override void _Ready()
{
var _parent = GetParent();
if (_parent is Control)
{
parent = (Control)_parent;
}
}
public override void _Process(double delta)
{
if (isDragging)
{
if (Input.IsMouseButtonPressed(MouseButton.Left))
{
Vector2 pos = GetViewport().GetMousePosition() - offset;
if (parent != null)
{
pos -= parent.GlobalPosition;
float _w = parent.Size.X - Size.X;
float _h = parent.Size.Y - Size.Y;
if (pos.X <= 0) pos.X = 0;
else if (pos.X > _w) pos.X = _w;
if (pos.Y <= 0) pos.Y = 0;
else if (pos.Y > _h) pos.Y = _h;
}
if (!movedToTop && parent != null)
{
parent.MoveChild(this, parent.GetChildCount());
movedToTop = true;
}
Position = pos;
}
else
{
isDragging = false;
movedToTop = false;
}
}
}
}