1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Visual scripting - Added pin disconnect with MMB

Visual scripting - Added min and max node
This commit is contained in:
Robert 2022-08-01 22:26:11 +02:00
parent b6e0c0fb66
commit dc17253518
15 changed files with 164 additions and 21 deletions

View File

@ -0,0 +1,43 @@
using System.Collections.Generic;
using Artemis.Core;
namespace Artemis.UI.Shared.Services.NodeEditor.Commands;
/// <summary>
/// Represents a node editor command that can be used to connect two pins.
/// </summary>
public class DisconnectPins : INodeEditorCommand
{
private readonly IPin _pin;
private readonly List<IPin> _originalConnections;
/// <summary>
/// Creates a new instance of the <see cref="DisconnectPins" /> class.
/// </summary>
/// <param name="pin">The pin to disconnect.</param>
public DisconnectPins(IPin pin)
{
_pin = pin;
_originalConnections = new List<IPin>(_pin.ConnectedTo);
}
#region Implementation of INodeEditorCommand
/// <inheritdoc />
public string DisplayName => "Disconnect pins";
/// <inheritdoc />
public void Execute()
{
_pin.DisconnectAll();
}
/// <inheritdoc />
public void Undo()
{
foreach (IPin pin in _originalConnections)
_pin.ConnectTo(pin);
}
#endregion
}

View File

@ -106,8 +106,8 @@ public interface INodeVmFactory : IVmFactory
NodeViewModel NodeViewModel(NodeScriptViewModel nodeScriptViewModel, INode node); NodeViewModel NodeViewModel(NodeScriptViewModel nodeScriptViewModel, INode node);
CableViewModel CableViewModel(NodeScriptViewModel nodeScriptViewModel, IPin from, IPin to); CableViewModel CableViewModel(NodeScriptViewModel nodeScriptViewModel, IPin from, IPin to);
DragCableViewModel DragCableViewModel(PinViewModel pinViewModel); DragCableViewModel DragCableViewModel(PinViewModel pinViewModel);
InputPinViewModel InputPinViewModel(IPin inputPin); InputPinViewModel InputPinViewModel(IPin inputPin, NodeScriptViewModel nodeScriptViewModel);
OutputPinViewModel OutputPinViewModel(IPin outputPin); OutputPinViewModel OutputPinViewModel(IPin outputPin, NodeScriptViewModel nodeScriptViewModel);
InputPinCollectionViewModel InputPinCollectionViewModel(IPinCollection inputPinCollection, NodeScriptViewModel nodeScriptViewModel); InputPinCollectionViewModel InputPinCollectionViewModel(IPinCollection inputPinCollection, NodeScriptViewModel nodeScriptViewModel);
OutputPinCollectionViewModel OutputPinCollectionViewModel(IPinCollection outputPinCollection, NodeScriptViewModel nodeScriptViewModel); OutputPinCollectionViewModel OutputPinCollectionViewModel(IPinCollection outputPinCollection, NodeScriptViewModel nodeScriptViewModel);
} }

View File

@ -47,12 +47,12 @@ public class NodeViewModel : ActivatableViewModelBase
// Create observable collections split up by direction // Create observable collections split up by direction
nodePins.Connect() nodePins.Connect()
.Filter(n => n.Direction == PinDirection.Input) .Filter(n => n.Direction == PinDirection.Input)
.Transform(p => (PinViewModel) nodeVmFactory.InputPinViewModel(p)) .Transform(p => (PinViewModel) nodeVmFactory.InputPinViewModel(p, nodeScriptViewModel))
.Bind(out ReadOnlyObservableCollection<PinViewModel> inputPins) .Bind(out ReadOnlyObservableCollection<PinViewModel> inputPins)
.Subscribe(); .Subscribe();
nodePins.Connect() nodePins.Connect()
.Filter(n => n.Direction == PinDirection.Output) .Filter(n => n.Direction == PinDirection.Output)
.Transform(p => (PinViewModel) nodeVmFactory.OutputPinViewModel(p)) .Transform(p => (PinViewModel) nodeVmFactory.OutputPinViewModel(p, nodeScriptViewModel))
.Bind(out ReadOnlyObservableCollection<PinViewModel> outputPins) .Bind(out ReadOnlyObservableCollection<PinViewModel> outputPins)
.Subscribe(); .Subscribe();
InputPinViewModels = inputPins; InputPinViewModels = inputPins;

View File

@ -6,19 +6,21 @@ namespace Artemis.UI.Screens.VisualScripting.Pins;
public class InputPinCollectionViewModel : PinCollectionViewModel public class InputPinCollectionViewModel : PinCollectionViewModel
{ {
private readonly NodeScriptViewModel _nodeScriptViewModel;
private readonly INodeVmFactory _nodeVmFactory; private readonly INodeVmFactory _nodeVmFactory;
public IPinCollection InputPinCollection { get; } public IPinCollection InputPinCollection { get; }
public InputPinCollectionViewModel(IPinCollection inputPinCollection, NodeScriptViewModel nodeScriptViewModel, INodeVmFactory nodeVmFactory, INodeEditorService nodeEditorService) public InputPinCollectionViewModel(IPinCollection inputPinCollection, NodeScriptViewModel nodeScriptViewModel, INodeVmFactory nodeVmFactory, INodeEditorService nodeEditorService)
: base(inputPinCollection, nodeScriptViewModel, nodeEditorService) : base(inputPinCollection, nodeScriptViewModel, nodeEditorService)
{ {
_nodeScriptViewModel = nodeScriptViewModel;
_nodeVmFactory = nodeVmFactory; _nodeVmFactory = nodeVmFactory;
InputPinCollection = inputPinCollection; InputPinCollection = inputPinCollection;
} }
protected override PinViewModel CreatePinViewModel(IPin pin) protected override PinViewModel CreatePinViewModel(IPin pin)
{ {
PinViewModel vm = _nodeVmFactory.InputPinViewModel(pin); PinViewModel vm = _nodeVmFactory.InputPinViewModel(pin, _nodeScriptViewModel);
vm.RemovePin = RemovePin; vm.RemovePin = RemovePin;
return vm; return vm;
} }

View File

@ -14,7 +14,7 @@
<UserControl.Styles> <UserControl.Styles>
<StyleInclude Source="/Screens/VisualScripting/VisualScripting.axaml" /> <StyleInclude Source="/Screens/VisualScripting/VisualScripting.axaml" />
</UserControl.Styles> </UserControl.Styles>
<StackPanel Name="PinContainer" Orientation="Horizontal" Spacing="6"> <StackPanel Name="PinContainer" Orientation="Horizontal" Spacing="6" PointerReleased="PinContainer_OnPointerReleased">
<Border Name="PinPoint"> <Border Name="PinPoint">
<Border Name="VisualPinPoint" <Border Name="VisualPinPoint"
BorderBrush="{CompiledBinding PinColor, Converter={StaticResource ColorToSolidColorBrushConverter}}" BorderBrush="{CompiledBinding PinColor, Converter={StaticResource ColorToSolidColorBrushConverter}}"

View File

@ -1,4 +1,6 @@
using System;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
namespace Artemis.UI.Screens.VisualScripting.Pins; namespace Artemis.UI.Screens.VisualScripting.Pins;
@ -15,4 +17,10 @@ public class InputPinView : PinView
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
} }
private void PinContainer_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
{
if (e.InitialPressMouseButton == MouseButton.Middle)
ViewModel?.DisconnectPin.Execute().Subscribe();
}
} }

View File

@ -1,11 +1,13 @@
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Shared.Services.NodeEditor;
namespace Artemis.UI.Screens.VisualScripting.Pins; namespace Artemis.UI.Screens.VisualScripting.Pins;
public class InputPinViewModel : PinViewModel public class InputPinViewModel : PinViewModel
{ {
public InputPinViewModel(IPin inputPin, INodeService nodeService) : base(inputPin, nodeService) public InputPinViewModel(IPin inputPin, NodeScriptViewModel nodeScriptViewModel, INodeService nodeService, INodeEditorService nodeEditorService)
: base(inputPin, nodeScriptViewModel, nodeService, nodeEditorService)
{ {
} }
} }

View File

@ -6,19 +6,21 @@ namespace Artemis.UI.Screens.VisualScripting.Pins;
public class OutputPinCollectionViewModel : PinCollectionViewModel public class OutputPinCollectionViewModel : PinCollectionViewModel
{ {
private readonly NodeScriptViewModel _nodeScriptViewModel;
private readonly INodeVmFactory _nodeVmFactory; private readonly INodeVmFactory _nodeVmFactory;
public IPinCollection OutputPinCollection { get; } public IPinCollection OutputPinCollection { get; }
public OutputPinCollectionViewModel(IPinCollection outputPinCollection, NodeScriptViewModel nodeScriptViewModel, INodeVmFactory nodeVmFactory, INodeEditorService nodeEditorService) public OutputPinCollectionViewModel(IPinCollection outputPinCollection, NodeScriptViewModel nodeScriptViewModel, INodeVmFactory nodeVmFactory, INodeEditorService nodeEditorService)
: base(outputPinCollection, nodeScriptViewModel, nodeEditorService) : base(outputPinCollection, nodeScriptViewModel, nodeEditorService)
{ {
_nodeScriptViewModel = nodeScriptViewModel;
_nodeVmFactory = nodeVmFactory; _nodeVmFactory = nodeVmFactory;
OutputPinCollection = outputPinCollection; OutputPinCollection = outputPinCollection;
} }
protected override PinViewModel CreatePinViewModel(IPin pin) protected override PinViewModel CreatePinViewModel(IPin pin)
{ {
PinViewModel vm = _nodeVmFactory.OutputPinViewModel(pin); PinViewModel vm = _nodeVmFactory.OutputPinViewModel(pin, _nodeScriptViewModel);
vm.RemovePin = RemovePin; vm.RemovePin = RemovePin;
return vm; return vm;
} }

View File

@ -14,7 +14,7 @@
<UserControl.Styles> <UserControl.Styles>
<StyleInclude Source="/Screens/VisualScripting/VisualScripting.axaml" /> <StyleInclude Source="/Screens/VisualScripting/VisualScripting.axaml" />
</UserControl.Styles> </UserControl.Styles>
<StackPanel Name="PinContainer" Orientation="Horizontal" Spacing="6"> <StackPanel Name="PinContainer" Orientation="Horizontal" Spacing="6" PointerReleased="PinContainer_OnPointerReleased">
<TextBlock Name="PinName" VerticalAlignment="Center" Text="{CompiledBinding Pin.Name}" /> <TextBlock Name="PinName" VerticalAlignment="Center" Text="{CompiledBinding Pin.Name}" />
<Border Name="PinPoint"> <Border Name="PinPoint">
<Border Name="VisualPinPoint" <Border Name="VisualPinPoint"

View File

@ -1,4 +1,6 @@
using System;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
namespace Artemis.UI.Screens.VisualScripting.Pins; namespace Artemis.UI.Screens.VisualScripting.Pins;
@ -15,4 +17,10 @@ public class OutputPinView : PinView
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
} }
private void PinContainer_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
{
if (e.InitialPressMouseButton == MouseButton.Middle)
ViewModel?.DisconnectPin.Execute().Subscribe();
}
} }

View File

@ -1,11 +1,13 @@
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Shared.Services.NodeEditor;
namespace Artemis.UI.Screens.VisualScripting.Pins; namespace Artemis.UI.Screens.VisualScripting.Pins;
public class OutputPinViewModel : PinViewModel public class OutputPinViewModel : PinViewModel
{ {
public OutputPinViewModel(IPin outputPin, INodeService nodeService) : base(outputPin, nodeService) public OutputPinViewModel(IPin outputPin, NodeScriptViewModel nodeScriptViewModel, INodeService nodeService, INodeEditorService nodeEditorService)
: base(outputPin, nodeScriptViewModel, nodeService, nodeEditorService)
{ {
} }
} }

View File

@ -6,6 +6,8 @@ using Artemis.Core.Events;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Extensions; using Artemis.UI.Shared.Extensions;
using Artemis.UI.Shared.Services.NodeEditor;
using Artemis.UI.Shared.Services.NodeEditor.Commands;
using Avalonia; using Avalonia;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;
using Avalonia.Media; using Avalonia.Media;
@ -22,11 +24,13 @@ public abstract class PinViewModel : ActivatableViewModelBase
private Color _pinColor; private Color _pinColor;
private Color _darkenedPinColor; private Color _darkenedPinColor;
protected PinViewModel(IPin pin, INodeService nodeService) protected PinViewModel(IPin pin, NodeScriptViewModel nodeScriptViewModel, INodeService nodeService, INodeEditorService nodeEditorService)
{ {
_nodeService = nodeService; _nodeService = nodeService;
Pin = pin; Pin = pin;
DisconnectPin = ReactiveCommand.Create(() => nodeEditorService.ExecuteCommand(nodeScriptViewModel.NodeScript, new DisconnectPins(Pin)));
SourceList<IPin> connectedPins = new(); SourceList<IPin> connectedPins = new();
this.WhenActivated(d => this.WhenActivated(d =>
{ {
@ -78,6 +82,8 @@ public abstract class PinViewModel : ActivatableViewModelBase
set => RaiseAndSetIfChanged(ref _removePin, value); set => RaiseAndSetIfChanged(ref _removePin, value);
} }
public ReactiveCommand<Unit, Unit> DisconnectPin { get; }
public bool IsCompatibleWith(PinViewModel pinViewModel) public bool IsCompatibleWith(PinViewModel pinViewModel)
{ {
if (pinViewModel.Pin.Direction == Pin.Direction || pinViewModel.Pin.Node == Pin.Node) if (pinViewModel.Pin.Direction == Pin.Direction || pinViewModel.Pin.Node == Pin.Node)

View File

@ -8,19 +8,19 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Avalonia" Version="0.10.15"/> <PackageReference Include="Avalonia" Version="0.10.15" />
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.15"/> <PackageReference Include="Avalonia.ReactiveUI" Version="0.10.15" />
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="0.10.14"/> <PackageReference Include="Avalonia.Xaml.Behaviors" Version="0.10.14" />
<PackageReference Include="Ninject" Version="3.3.4"/> <PackageReference Include="Ninject" Version="3.3.4" />
<PackageReference Include="NoStringEvaluating" Version="2.2.2"/> <PackageReference Include="NoStringEvaluating" Version="2.2.2" />
<PackageReference Include="ReactiveUI" Version="17.1.50"/> <PackageReference Include="ReactiveUI" Version="17.1.50" />
<PackageReference Include="ReactiveUI.Validation" Version="2.2.1"/> <PackageReference Include="ReactiveUI.Validation" Version="2.2.1" />
<PackageReference Include="SkiaSharp" Version="2.88.1-preview.1"/> <PackageReference Include="SkiaSharp" Version="2.88.1-preview.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj"/> <ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
<ProjectReference Include="..\Artemis.UI.Shared\Artemis.UI.Shared.csproj"/> <ProjectReference Include="..\Artemis.UI.Shared\Artemis.UI.Shared.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -0,0 +1,35 @@
using Artemis.Core;
namespace Artemis.VisualScripting.Nodes.Mathematics;
[Node("Max", "Outputs the largest of the connected numeric values.", "Mathematics", InputType = typeof(Numeric), OutputType = typeof(Numeric))]
public class MaxNumericsNode : Node
{
#region Constructors
public MaxNumericsNode()
: base("Max", "Outputs the largest of the connected numeric values.")
{
Values = CreateInputPinCollection<Numeric>("Values", 2);
Max = CreateOutputPin<Numeric>("Max");
}
#endregion
#region Methods
public override void Evaluate()
{
Max.Value = Values.Values.Max();
}
#endregion
#region Properties & Fields
public InputPinCollection<Numeric> Values { get; }
public OutputPin<Numeric> Max { get; }
#endregion
}

View File

@ -0,0 +1,35 @@
using Artemis.Core;
namespace Artemis.VisualScripting.Nodes.Mathematics;
[Node("Min", "Outputs the smallest of the connected numeric values.", "Mathematics", InputType = typeof(Numeric), OutputType = typeof(Numeric))]
public class MinNumericsNode : Node
{
#region Constructors
public MinNumericsNode()
: base("Min", "Outputs the smallest of the connected numeric values.")
{
Values = CreateInputPinCollection<Numeric>("Values", 2);
Min = CreateOutputPin<Numeric>("Min");
}
#endregion
#region Methods
public override void Evaluate()
{
Min.Value = Values.Values.Min();
}
#endregion
#region Properties & Fields
public InputPinCollection<Numeric> Values { get; }
public OutputPin<Numeric> Min { get; }
#endregion
}