1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00
2022-03-19 22:58:14 +01:00

162 lines
6.1 KiB
C#

using System;
using System.Collections.ObjectModel;
using System.Reactive;
using System.Reactive.Linq;
using Artemis.Core;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.VisualScripting.Pins;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services.NodeEditor;
using Artemis.UI.Shared.Services.NodeEditor.Commands;
using Avalonia;
using Avalonia.Controls.Mixins;
using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
namespace Artemis.UI.Screens.VisualScripting;
public class NodeViewModel : ActivatableViewModelBase
{
private readonly INodeEditorService _nodeEditorService;
private ICustomNodeViewModel? _customNodeViewModel;
private ReactiveCommand<Unit, Unit>? _deleteNode;
private double _dragOffsetX;
private double _dragOffsetY;
private bool _isSelected;
private ObservableAsPropertyHelper<bool>? _isStaticNode;
private double _startX;
private double _startY;
public NodeViewModel(NodeScriptViewModel nodeScriptViewModel, INode node, INodePinVmFactory nodePinVmFactory, INodeEditorService nodeEditorService)
{
NodeScriptViewModel = nodeScriptViewModel;
_nodeEditorService = nodeEditorService;
Node = node;
DeleteNode = ReactiveCommand.Create(ExecuteDeleteNode, this.WhenAnyValue(vm => vm.IsStaticNode).Select(v => !v));
SourceList<IPin> nodePins = new();
SourceList<IPinCollection> nodePinCollections = new();
// Create observable collections split up by direction
nodePins.Connect()
.Filter(n => n.Direction == PinDirection.Input)
.Transform(nodePinVmFactory.InputPinViewModel)
.Bind(out ReadOnlyObservableCollection<PinViewModel> inputPins)
.Subscribe();
nodePins.Connect()
.Filter(n => n.Direction == PinDirection.Output)
.Transform(nodePinVmFactory.OutputPinViewModel)
.Bind(out ReadOnlyObservableCollection<PinViewModel> outputPins)
.Subscribe();
InputPinViewModels = inputPins;
OutputPinViewModels = outputPins;
// Same again but for pin collections
nodePinCollections.Connect()
.Filter(n => n.Direction == PinDirection.Input)
.Transform(nodePinVmFactory.InputPinCollectionViewModel)
.Bind(out ReadOnlyObservableCollection<PinCollectionViewModel> inputPinCollections)
.Subscribe();
nodePinCollections.Connect()
.Filter(n => n.Direction == PinDirection.Output)
.Transform(nodePinVmFactory.OutputPinCollectionViewModel)
.Bind(out ReadOnlyObservableCollection<PinCollectionViewModel> outputPinCollections)
.Subscribe();
InputPinCollectionViewModels = inputPinCollections;
OutputPinCollectionViewModels = outputPinCollections;
// Create a single observable collection containing all pin view models
InputPinViewModels.ToObservableChangeSet()
.Merge(OutputPinViewModels.ToObservableChangeSet())
.Merge(InputPinCollectionViewModels.ToObservableChangeSet().TransformMany(c => c.PinViewModels))
.Merge(OutputPinCollectionViewModels.ToObservableChangeSet().TransformMany(c => c.PinViewModels))
.Bind(out ReadOnlyObservableCollection<PinViewModel> pins)
.Subscribe();
PinViewModels = pins;
this.WhenActivated(d =>
{
_isStaticNode = Node.WhenAnyValue(n => n.IsDefaultNode, n => n.IsExitNode)
.Select(tuple => tuple.Item1 || tuple.Item2)
.ToProperty(this, model => model.IsStaticNode)
.DisposeWith(d);
// Subscribe to pin changes
Node.WhenAnyValue(n => n.Pins).Subscribe(p => nodePins.Edit(source =>
{
source.Clear();
source.AddRange(p);
})).DisposeWith(d);
// Subscribe to pin collection changes
Node.WhenAnyValue(n => n.PinCollections).Subscribe(c => nodePinCollections.Edit(source =>
{
source.Clear();
source.AddRange(c);
})).DisposeWith(d);
});
}
public bool IsStaticNode => _isStaticNode?.Value ?? true;
public NodeScriptViewModel NodeScriptViewModel { get; }
public INode Node { get; }
public ReadOnlyObservableCollection<PinViewModel> InputPinViewModels { get; }
public ReadOnlyObservableCollection<PinCollectionViewModel> InputPinCollectionViewModels { get; }
public ReadOnlyObservableCollection<PinViewModel> OutputPinViewModels { get; }
public ReadOnlyObservableCollection<PinCollectionViewModel> OutputPinCollectionViewModels { get; }
public ReadOnlyObservableCollection<PinViewModel> PinViewModels { get; }
public ICustomNodeViewModel? CustomNodeViewModel
{
get => _customNodeViewModel;
set => RaiseAndSetIfChanged(ref _customNodeViewModel, value);
}
public ReactiveCommand<Unit, Unit>? DeleteNode
{
get => _deleteNode;
set => RaiseAndSetIfChanged(ref _deleteNode, value);
}
public bool IsSelected
{
get => _isSelected;
set => RaiseAndSetIfChanged(ref _isSelected, value);
}
public void StartDrag(Point mouseStartPosition)
{
if (!IsSelected)
return;
_dragOffsetX = Node.X - mouseStartPosition.X;
_dragOffsetY = Node.Y - mouseStartPosition.Y;
_startX = Node.X;
_startY = Node.Y;
}
public void UpdateDrag(Point mousePosition)
{
if (!IsSelected)
return;
Node.X = Math.Round((mousePosition.X + _dragOffsetX) / 10d, 0, MidpointRounding.AwayFromZero) * 10d;
Node.Y = Math.Round((mousePosition.Y + _dragOffsetY) / 10d, 0, MidpointRounding.AwayFromZero) * 10d;
}
public MoveNode? FinishDrag()
{
if (IsSelected && (Math.Abs(_startX - Node.X) > 0.01 || Math.Abs(_startY - Node.Y) > 0.01))
return new MoveNode(Node, Node.X, Node.Y, _startX, _startY);
return null;
}
private void ExecuteDeleteNode()
{
_nodeEditorService.ExecuteCommand(NodeScriptViewModel.NodeScript, new DeleteNode(NodeScriptViewModel.NodeScript, Node));
}
}