Major Changes including Options menu, logic blocks, modding system, and more.
This commit is contained in:
406
src/ReorderableContainer/ReOrderableContainer.cs
Normal file
406
src/ReorderableContainer/ReOrderableContainer.cs
Normal file
@@ -0,0 +1,406 @@
|
||||
//https://github.com/FoolLin/ReorderableContainer
|
||||
namespace FoodFactory;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.GodotNodeInterfaces;
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
|
||||
public interface IReOrderableContainer : IContainer
|
||||
{
|
||||
|
||||
}
|
||||
[Meta(typeof(IAutoNode))]
|
||||
[Tool]
|
||||
public partial class ReOrderableContainer : Container, IReOrderableContainer
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
|
||||
[Signal] public delegate void ReOrderedEventHandler(int from, int to);
|
||||
[Export] public double HoldDuration { get; set; } = .5;
|
||||
[Export(PropertyHint.Range, "3,30,0.01,or_greater,or_less")] public double Speed { get; set; } = 10;
|
||||
[Export] public double Separation
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
if (value == Separation || value < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
field = value;
|
||||
OnSortChildren();
|
||||
}
|
||||
}
|
||||
[Export]
|
||||
public bool IsVertical
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
if (value == IsVertical)
|
||||
{
|
||||
return;
|
||||
}
|
||||
field = value;
|
||||
if (IsVertical)
|
||||
{
|
||||
CustomMinimumSize = CustomMinimumSize with { X = 0 };
|
||||
}
|
||||
else
|
||||
{
|
||||
CustomMinimumSize = CustomMinimumSize with { Y = 0 };
|
||||
}
|
||||
OnSortChildren();
|
||||
}
|
||||
}
|
||||
[Export] public ScrollContainer ScrollContainer { get; set; } = default!;
|
||||
[Export] public double AutoScrollSpeed { get; set; } = 10;
|
||||
[Export(PropertyHint.Range, "0,.5")] public double AutoScrollRange { get; set; } = .3;
|
||||
[Export] public double ScrollThreshold { get; set; } = 30;
|
||||
[Export] public bool IsDebugging { get; set; } = false;
|
||||
|
||||
private double _scrollStartingPoint = 0;
|
||||
// private bool _isSmoothScroll = false;
|
||||
|
||||
private readonly List<Rect2> _dropZones = [];
|
||||
private int _dropZoneIndex = -1;
|
||||
private readonly List<Rect2> _expectChildRect = [];
|
||||
|
||||
protected Control? _focusChild = null;
|
||||
protected bool _isPress = false;
|
||||
protected bool _isHold = false;
|
||||
private double _currentDuration = 0.0;
|
||||
private bool _isUsingProcess = false;
|
||||
private const int DROP_ZONE_EXTEND = 2000;
|
||||
|
||||
public void OnReady()
|
||||
{
|
||||
if (ScrollContainer is null && GetParent() is ScrollContainer)
|
||||
{
|
||||
ScrollContainer = GetParent<ScrollContainer>();
|
||||
}
|
||||
|
||||
// if scroll_container != null and scroll_container.has_method("handle_overdrag"):
|
||||
// _is_smooth_scroll = true
|
||||
ProcessMode = ProcessModeEnum.Pausable;
|
||||
AdjustExpectedChildRect();
|
||||
|
||||
SortChildren += OnSortChildrenWrapper;
|
||||
// GetTree().NodeAdded += OnNodeAdded;
|
||||
}
|
||||
public override void _GuiInput(InputEvent @event)
|
||||
{
|
||||
if (@event is InputEventMouseButton eventMouseButton && eventMouseButton.ButtonIndex == MouseButton.Left)
|
||||
{
|
||||
GD.Print(string.Join(',',((IReOrderableContainer)this).GetChildren().Select(s => $"{s}, {s.Name}, {s.GetType()}, {(Control)s}")));
|
||||
foreach (var child in ((IReOrderableContainer)this).GetChildren().OfType<Control>())
|
||||
{
|
||||
if (child.GetRect().HasPoint(GetLocalMousePosition()) && eventMouseButton.IsPressed())
|
||||
{
|
||||
_focusChild = child;
|
||||
_isPress = true;
|
||||
}
|
||||
else if (!eventMouseButton.Pressed)
|
||||
{
|
||||
_isPress = false;
|
||||
_isHold = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
if (Engine.IsEditorHint())
|
||||
{
|
||||
return;
|
||||
}
|
||||
HandleInput(delta);
|
||||
if ((_currentDuration >= HoldDuration) != _isHold)
|
||||
{
|
||||
_isHold = _currentDuration >= HoldDuration;
|
||||
if (_isHold)
|
||||
{
|
||||
OnStartDragging();
|
||||
}
|
||||
}
|
||||
if (_isHold)
|
||||
{
|
||||
HandleDraggingChildPos(delta);
|
||||
if (ScrollContainer is not null)
|
||||
{
|
||||
HandleAutoScroll(delta);
|
||||
}
|
||||
}
|
||||
else if (!_isHold && _dropZoneIndex != -1)
|
||||
{
|
||||
OnStopDragging();
|
||||
}
|
||||
if (_isUsingProcess)
|
||||
{
|
||||
OnSortChildren(delta);
|
||||
}
|
||||
QueueRedraw();
|
||||
}
|
||||
public override void _Draw()
|
||||
{
|
||||
var r = new Random(0);
|
||||
foreach (var item in _dropZones)
|
||||
{
|
||||
// GD.Print(item);
|
||||
DrawRect(item,new Color(r.NextSingle(),r.NextSingle(),r.NextSingle()));
|
||||
}
|
||||
base._Draw();
|
||||
}
|
||||
|
||||
private void HandleInput(double delta)
|
||||
{
|
||||
if (ScrollContainer is not null && _isPress && !_isHold)
|
||||
{
|
||||
var scrollPoint = IsVertical ? ScrollContainer.ScrollVertical : ScrollContainer.ScrollHorizontal;
|
||||
if (_currentDuration == 0)
|
||||
{
|
||||
_scrollStartingPoint = scrollPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
_isPress = Mathf.Abs(scrollPoint - _scrollStartingPoint) <= ScrollThreshold;
|
||||
}
|
||||
}
|
||||
_currentDuration = _isPress ? _currentDuration + delta : 0;
|
||||
}
|
||||
private void OnStartDragging()
|
||||
{
|
||||
_isUsingProcess = true;
|
||||
_focusChild?.ZIndex = 1;
|
||||
// if _is_smooth_scroll:
|
||||
// scroll_container.process_mode = Node.PROCESS_MODE_DISABLED
|
||||
foreach (var child in GetVisibleChildren())
|
||||
{
|
||||
child.PropagateCall(Control.MethodName.SetMouseFilter, [Variant.From(MouseFilterEnum.Ignore)]);
|
||||
}
|
||||
}
|
||||
private void OnStopDragging()
|
||||
{
|
||||
if (_focusChild is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_focusChild.ZIndex = 0;
|
||||
var focusChildIndex = _focusChild.GetIndex();
|
||||
AsIReOrderableContainer().MoveChild(_focusChild, _dropZoneIndex);
|
||||
EmitSignalReOrdered(focusChildIndex, _dropZoneIndex);
|
||||
_focusChild = null;
|
||||
GD.Print(_dropZoneIndex);
|
||||
_dropZoneIndex = -1;
|
||||
// if _is_smooth_scroll:
|
||||
// scroll_container.pos = -Vector2(scroll_container.scroll_horizontal, scroll_container.scroll_vertical)
|
||||
// scroll_container.process_mode = Node.PROCESS_MODE_INHERIT
|
||||
foreach (var child in GetVisibleChildren())
|
||||
{
|
||||
child.PropagateCall(Control.MethodName.SetMouseFilter,[Variant.From(MouseFilterEnum.Pass)]);
|
||||
}
|
||||
}
|
||||
public void HandleDraggingChildPos(double delta)
|
||||
{
|
||||
if (IsVertical)
|
||||
{
|
||||
var targetPos = GetLocalMousePosition().Y - (_focusChild.Size.Y/2);
|
||||
_focusChild.Position = _focusChild.Position with {Y = Mathf.Lerp(_focusChild.Position.Y, targetPos, (float)(delta * Speed))};
|
||||
}
|
||||
else
|
||||
{
|
||||
var targetPos = GetLocalMousePosition().X - (_focusChild.Size.X/2);
|
||||
_focusChild.Position = _focusChild.Position with {X = Mathf.Lerp(_focusChild.Position.X, targetPos, (float)(delta * Speed))};
|
||||
}
|
||||
var childCenterPos = _focusChild.GetRect().GetCenter();
|
||||
for (int i = 0; i < _dropZones.Count; i++)
|
||||
{
|
||||
var dropZone = _dropZones[i];
|
||||
GD.Print(Dump.With(i).And(_dropZones[i]).And(childCenterPos).And(dropZone.HasPoint(childCenterPos)).And(_dropZones.Count));
|
||||
if (dropZone.HasPoint(childCenterPos))
|
||||
{
|
||||
_dropZoneIndex = i;
|
||||
break;
|
||||
}
|
||||
else if (i == _dropZones.Count - 1)
|
||||
{
|
||||
_dropZoneIndex = -1;
|
||||
}
|
||||
// elif i == _drop_zones.size() - 1:
|
||||
// _drop_zone_index = -1
|
||||
|
||||
}
|
||||
}
|
||||
public void HandleAutoScroll(double delta)
|
||||
{
|
||||
var mouseGPos = GetGlobalMousePosition();
|
||||
var scrollGRect = ScrollContainer.GetGlobalRect();
|
||||
var index = IsVertical ? 0 : 1;
|
||||
var leftUpper = scrollGRect.Position[index] + (scrollGRect.Size[index] * AutoScrollRange);
|
||||
var rightLower = scrollGRect.Position[index] + (scrollGRect.Size[index] * (1 - AutoScrollRange));
|
||||
|
||||
setScroll(
|
||||
mouseGPos[index] switch
|
||||
{
|
||||
var i when leftUpper > i => scroll => useDelta((leftUpper - mouseGPos[index]) / (leftUpper - scrollGRect.Position[index])),
|
||||
var i when rightLower < i => scroll => useDelta((mouseGPos[index] - rightLower) / (scrollGRect.Position[index] - rightLower)),
|
||||
var i => scroll => (int)scroll
|
||||
}
|
||||
);
|
||||
int useDelta(double d) => (int)(delta * AutoScrollSpeed * 150.0 * d);
|
||||
void setScroll(Func<double, int> scroll)
|
||||
{
|
||||
if (IsVertical)
|
||||
{
|
||||
ScrollContainer.ScrollVertical = scroll(ScrollContainer.ScrollVertical);
|
||||
}
|
||||
else
|
||||
{
|
||||
ScrollContainer.ScrollHorizontal = scroll(ScrollContainer.ScrollHorizontal);
|
||||
}
|
||||
}
|
||||
}
|
||||
public void OnExitTree()
|
||||
{
|
||||
if (Engine.IsEditorHint())
|
||||
{
|
||||
return;
|
||||
}
|
||||
SortChildren -= OnSortChildrenWrapper;
|
||||
// GetTree().NodeAdded -= OnNodeAdded;
|
||||
}
|
||||
private void OnNodeAdded(Node node)
|
||||
{
|
||||
if (node is Control control && !Engine.IsEditorHint())
|
||||
{
|
||||
control.MouseFilter = MouseFilterEnum.Pass;
|
||||
}
|
||||
}
|
||||
private void OnSortChildrenWrapper() => OnSortChildren();
|
||||
private void OnSortChildren(double delta = -1)
|
||||
{
|
||||
if (_isUsingProcess && delta == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
AdjustExpectedChildRect();
|
||||
AdjustChildRect(delta);
|
||||
AdjustDropZoneRect();
|
||||
}
|
||||
private void AdjustExpectedChildRect()
|
||||
{
|
||||
_expectChildRect.Clear();
|
||||
var endPoint = 0.0;
|
||||
var i = 0;
|
||||
var index = IsVertical ? 1 : 0;
|
||||
foreach (var child in GetVisibleChildren())
|
||||
{
|
||||
var minSize = child.GetCombinedMinimumSize();
|
||||
if (i == _dropZoneIndex)
|
||||
{
|
||||
endPoint += _focusChild.Size[index] + Separation;
|
||||
}
|
||||
_expectChildRect.Add(
|
||||
new(
|
||||
IsVertical
|
||||
? new(0, (float)endPoint)
|
||||
: new((float)endPoint, 0)
|
||||
, IsVertical
|
||||
? new(Size.X, minSize.Y)
|
||||
: new(minSize.X, Size.Y)));
|
||||
endPoint += minSize[index] + Separation;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
private void AdjustChildRect(double delta = -1)
|
||||
{
|
||||
if (!GetVisibleChildren().Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var isAnimating = false;
|
||||
var endPoint = 0.0;
|
||||
var children = GetVisibleChildren().ToArray();
|
||||
for (int i = 0; i < children.Length; i++)
|
||||
{
|
||||
var child = children[i];
|
||||
if (child.Position == _expectChildRect[i].Position && child.Size == _expectChildRect[i].Size)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (_isUsingProcess)
|
||||
{
|
||||
isAnimating = true;
|
||||
child.Position = child.Position.Lerp(_expectChildRect[i].Position, (float)(delta * Speed));
|
||||
child.Size = _expectChildRect[i].Size;
|
||||
if ((child.Position - _expectChildRect[i].Position).Length() <= 1)
|
||||
{
|
||||
child.Position = _expectChildRect[i].Position;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
child.Position = _expectChildRect[i].Position;
|
||||
child.Size = _expectChildRect[i].Size;
|
||||
}
|
||||
var lastChild = children[^1];
|
||||
|
||||
var index = IsVertical ? 0 : 1;
|
||||
var newCustomSize =
|
||||
_isUsingProcess && _dropZoneIndex == children.Length
|
||||
? _expectChildRect[^1].End[index] + _focusChild.Size[index] + Separation
|
||||
: !_isUsingProcess
|
||||
?lastChild.GetRect().End[index]
|
||||
:lastChild.CustomMinimumSize[index];
|
||||
// lastChild.CustomMinimumSize = IsVertical?lastChild.CustomMinimumSize with { Y = (float)newCustomSize} :lastChild.CustomMinimumSize with { X = (float)newCustomSize};
|
||||
if (!isAnimating && _focusChild is not null)
|
||||
{
|
||||
_isUsingProcess = false;
|
||||
}
|
||||
}
|
||||
private void AdjustDropZoneRect()
|
||||
{
|
||||
_dropZones.Clear();
|
||||
if (!GetVisibleChildren().Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var children = GetVisibleChildren().ToArray();
|
||||
var minSize = children.Max(child => child.GetMinimumSize()[IsVertical?0:1]);
|
||||
if (_focusChild is not null)
|
||||
{
|
||||
minSize = Mathf.Max(minSize,_focusChild.GetMinimumSize()[IsVertical?0:1]);
|
||||
}
|
||||
for (int i = 0; i < children.Length; i++)
|
||||
{
|
||||
var child = children[i];
|
||||
_dropZones.Add(i == 0 ?
|
||||
new Rect2()
|
||||
{
|
||||
Position = child.Position - (IsVertical?new(0,DROP_ZONE_EXTEND):new(DROP_ZONE_EXTEND,0)),
|
||||
End = IsVertical? new(minSize,child.GetRect().GetCenter().Y):new(child.GetRect().GetCenter().X,minSize)
|
||||
}
|
||||
:
|
||||
new Rect2()
|
||||
{
|
||||
Position = IsVertical?new(children[i-1].Position.X,children[i-1].GetRect().GetCenter().Y) : new(children[i-1].GetRect().GetCenter().X,children[i-1].Position.Y),
|
||||
End = IsVertical?new(minSize,child.GetRect().GetCenter().Y) : new(child.GetRect().GetCenter().X,minSize),
|
||||
});
|
||||
if (i == children.Length - 1)
|
||||
{
|
||||
_dropZones.Add(new Rect2()
|
||||
{
|
||||
Position = IsVertical?new(child.Position.X,child.GetRect().GetCenter().Y):new(child.GetRect().GetCenter().X,child.Position.Y),
|
||||
End = IsVertical? new(minSize,child.GetRect().End.Y + DROP_ZONE_EXTEND):new(child.GetRect().End.X + DROP_ZONE_EXTEND, minSize)
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
private IReOrderableContainer AsIReOrderableContainer() => (IReOrderableContainer)this;
|
||||
private IEnumerable<Control> GetVisibleChildren() => GetChildren().OfType<Control>().Where(child => child.Visible && !(child == _focusChild && _isHold));
|
||||
}
|
||||
1
src/ReorderableContainer/ReOrderableContainer.cs.uid
Normal file
1
src/ReorderableContainer/ReOrderableContainer.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dcarxou4fv5k
|
||||
65
src/ReorderableContainer/ReOrderableContainerFancy.cs
Normal file
65
src/ReorderableContainer/ReOrderableContainerFancy.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
namespace FoodFactory;
|
||||
|
||||
using System.Linq;
|
||||
using Chickensoft.GodotNodeInterfaces;
|
||||
using Godot;
|
||||
|
||||
public interface IReOrderableContainerFancy: IReOrderableContainer
|
||||
{
|
||||
|
||||
}
|
||||
public partial class ReOrderableContainerFancy : ReOrderableContainer, IReOrderableContainerFancy
|
||||
{
|
||||
public override void _GuiInput(InputEvent @event)
|
||||
{
|
||||
if (@event is InputEventMouseButton eventMouseButton && eventMouseButton.ButtonIndex == MouseButton.Left)
|
||||
{
|
||||
foreach (var child in (this as IReOrderableContainerFancy).GetChildren().OfType<Control>())
|
||||
{
|
||||
if (child is OrderableItem orderableItem)
|
||||
{
|
||||
if (orderableItem.DragHandel.GetRect().HasPoint(child.GetLocalMousePosition()) && eventMouseButton.IsPressed())
|
||||
{
|
||||
_focusChild = orderableItem;
|
||||
_isPress = true;
|
||||
}
|
||||
else if (!eventMouseButton.Pressed)
|
||||
{
|
||||
_isPress = false;
|
||||
_isHold = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (child.GetRect().HasPoint(child.GetLocalMousePosition()) && eventMouseButton.IsPressed())
|
||||
{
|
||||
_focusChild = child;
|
||||
_isPress = true;
|
||||
}
|
||||
else if (!eventMouseButton.Pressed)
|
||||
{
|
||||
_isPress = false;
|
||||
_isHold = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public interface IOrderableItem : IControl
|
||||
{
|
||||
IControl DragHandel { get; }
|
||||
}
|
||||
public partial class OrderableItem : Control, IOrderableItem
|
||||
{
|
||||
[Export] public Control DragHandel { get; set; } = default!;
|
||||
|
||||
IControl IOrderableItem.DragHandel => (IControl)DragHandel;
|
||||
public override void _Ready()
|
||||
{
|
||||
if (DragHandel is null)
|
||||
{
|
||||
DragHandel = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://mb7j1wlyulw6
|
||||
Reference in New Issue
Block a user