mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Node editor - Added node collection pin add/remove
Node editor - Added first static value node
This commit is contained in:
parent
2907b86174
commit
d9d237e0eb
@ -43,6 +43,10 @@ namespace Artemis.Core.Services
|
|||||||
if (match != null)
|
if (match != null)
|
||||||
return match;
|
return match;
|
||||||
|
|
||||||
|
// Objects represent an input that can take any type, these are hardcoded white
|
||||||
|
if (type == typeof(object))
|
||||||
|
return new TypeColorRegistration(type, new SKColor(255, 255, 255, 255), Constants.CorePlugin);
|
||||||
|
|
||||||
// Come up with a random color based on the type name that should be the same each time
|
// Come up with a random color based on the type name that should be the same each time
|
||||||
MD5 md5Hasher = MD5.Create();
|
MD5 md5Hasher = MD5.Create();
|
||||||
byte[] hashed = md5Hasher.ComputeHash(Encoding.UTF8.GetBytes(type.FullName!));
|
byte[] hashed = md5Hasher.ComputeHash(Encoding.UTF8.GetBytes(type.FullName!));
|
||||||
|
|||||||
@ -11,7 +11,7 @@ namespace Artemis.Core
|
|||||||
/// Usage outside that context is not recommended due to conversion overhead.
|
/// Usage outside that context is not recommended due to conversion overhead.
|
||||||
/// </para>
|
/// </para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly struct Numeric : IComparable<Numeric>
|
public readonly struct Numeric : IComparable<Numeric>, IConvertible
|
||||||
{
|
{
|
||||||
private readonly float _value;
|
private readonly float _value;
|
||||||
|
|
||||||
@ -140,6 +140,11 @@ namespace Artemis.Core
|
|||||||
return p._value;
|
return p._value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static implicit operator decimal(Numeric p)
|
||||||
|
{
|
||||||
|
return (decimal) p._value;
|
||||||
|
}
|
||||||
|
|
||||||
public static implicit operator byte(Numeric p)
|
public static implicit operator byte(Numeric p)
|
||||||
{
|
{
|
||||||
return (byte) Math.Clamp(p._value, 0, 255);
|
return (byte) Math.Clamp(p._value, 0, 255);
|
||||||
@ -260,6 +265,112 @@ namespace Artemis.Core
|
|||||||
type == typeof(int) ||
|
type == typeof(int) ||
|
||||||
type == typeof(byte);
|
type == typeof(byte);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Implementation of IConvertible
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public TypeCode GetTypeCode()
|
||||||
|
{
|
||||||
|
return _value.GetTypeCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool ToBoolean(IFormatProvider? provider)
|
||||||
|
{
|
||||||
|
return Convert.ToBoolean(_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public byte ToByte(IFormatProvider? provider)
|
||||||
|
{
|
||||||
|
return (byte) Math.Clamp(_value, 0, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public char ToChar(IFormatProvider? provider)
|
||||||
|
{
|
||||||
|
return Convert.ToChar(_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public DateTime ToDateTime(IFormatProvider? provider)
|
||||||
|
{
|
||||||
|
return Convert.ToDateTime(_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public decimal ToDecimal(IFormatProvider? provider)
|
||||||
|
{
|
||||||
|
return (decimal) _value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public double ToDouble(IFormatProvider? provider)
|
||||||
|
{
|
||||||
|
return _value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public short ToInt16(IFormatProvider? provider)
|
||||||
|
{
|
||||||
|
return (short) MathF.Round(_value, MidpointRounding.AwayFromZero);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int ToInt32(IFormatProvider? provider)
|
||||||
|
{
|
||||||
|
return (int) MathF.Round(_value, MidpointRounding.AwayFromZero);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public long ToInt64(IFormatProvider? provider)
|
||||||
|
{
|
||||||
|
return (long) MathF.Round(_value, MidpointRounding.AwayFromZero);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public sbyte ToSByte(IFormatProvider? provider)
|
||||||
|
{
|
||||||
|
return (sbyte) Math.Clamp(_value, 0, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public float ToSingle(IFormatProvider? provider)
|
||||||
|
{
|
||||||
|
return _value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string ToString(IFormatProvider? provider)
|
||||||
|
{
|
||||||
|
return _value.ToString(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public object ToType(Type conversionType, IFormatProvider? provider)
|
||||||
|
{
|
||||||
|
return Convert.ChangeType(_value, conversionType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ushort ToUInt16(IFormatProvider? provider)
|
||||||
|
{
|
||||||
|
return (ushort) MathF.Round(_value, MidpointRounding.AwayFromZero);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public uint ToUInt32(IFormatProvider? provider)
|
||||||
|
{
|
||||||
|
return (uint) MathF.Round(_value, MidpointRounding.AwayFromZero);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ulong ToUInt64(IFormatProvider? provider)
|
||||||
|
{
|
||||||
|
return (ulong) MathF.Round(_value, MidpointRounding.AwayFromZero);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -279,7 +390,8 @@ namespace Artemis.Core
|
|||||||
if (source == null) throw new ArgumentNullException(nameof(source));
|
if (source == null) throw new ArgumentNullException(nameof(source));
|
||||||
|
|
||||||
float sum = 0;
|
float sum = 0;
|
||||||
foreach (float v in source) sum += v;
|
foreach (float v in source)
|
||||||
|
sum += v;
|
||||||
|
|
||||||
return new Numeric(sum);
|
return new Numeric(sum);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,8 +14,11 @@ namespace Artemis.Core
|
|||||||
#region Constructors
|
#region Constructors
|
||||||
|
|
||||||
internal InputPinCollection(INode node, string name, int initialCount)
|
internal InputPinCollection(INode node, string name, int initialCount)
|
||||||
: base(node, name, initialCount)
|
: base(node, name)
|
||||||
{
|
{
|
||||||
|
// Can't do this in the base constructor because the type won't be set yet
|
||||||
|
for (int i = 0; i < initialCount; i++)
|
||||||
|
Add(CreatePin());
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -23,7 +26,7 @@ namespace Artemis.Core
|
|||||||
#region Methods
|
#region Methods
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override IPin CreatePin()
|
public override IPin CreatePin()
|
||||||
{
|
{
|
||||||
return new InputPin<T>(Node, string.Empty);
|
return new InputPin<T>(Node, string.Empty);
|
||||||
}
|
}
|
||||||
@ -59,13 +62,13 @@ namespace Artemis.Core
|
|||||||
#region Constructors
|
#region Constructors
|
||||||
|
|
||||||
internal InputPinCollection(INode node, Type type, string name, int initialCount)
|
internal InputPinCollection(INode node, Type type, string name, int initialCount)
|
||||||
: base(node, name, 0)
|
: base(node, name)
|
||||||
{
|
{
|
||||||
Type = type;
|
Type = type;
|
||||||
|
|
||||||
// Can't do this in the base constructor because the type won't be set yet
|
// Can't do this in the base constructor because the type won't be set yet
|
||||||
for (int i = 0; i < initialCount; i++)
|
for (int i = 0; i < initialCount; i++)
|
||||||
AddPin();
|
Add(CreatePin());
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -73,7 +76,7 @@ namespace Artemis.Core
|
|||||||
#region Methods
|
#region Methods
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override IPin CreatePin()
|
public override IPin CreatePin()
|
||||||
{
|
{
|
||||||
return new InputPin(Node, Type, string.Empty);
|
return new InputPin(Node, Type, string.Empty);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,10 +40,15 @@ namespace Artemis.Core
|
|||||||
event EventHandler<SingleValueEventArgs<IPin>> PinRemoved;
|
event EventHandler<SingleValueEventArgs<IPin>> PinRemoved;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new pin and adds it to the collection
|
/// Creates a new pin compatible with this collection
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The newly added pin</returns>
|
/// <returns>The newly created pin</returns>
|
||||||
IPin AddPin();
|
IPin CreatePin();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the provided <paramref name="pin" /> to the collection
|
||||||
|
/// </summary>
|
||||||
|
void Add(IPin pin);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes the provided <paramref name="pin" /> from the collection
|
/// Removes the provided <paramref name="pin" /> from the collection
|
||||||
|
|||||||
@ -199,7 +199,7 @@ namespace Artemis.Core
|
|||||||
while (collection.Count() > entityNodePinCollection.Amount)
|
while (collection.Count() > entityNodePinCollection.Amount)
|
||||||
collection.Remove(collection.Last());
|
collection.Remove(collection.Last());
|
||||||
while (collection.Count() < entityNodePinCollection.Amount)
|
while (collection.Count() < entityNodePinCollection.Amount)
|
||||||
collection.AddPin();
|
collection.Add(collection.CreatePin());
|
||||||
}
|
}
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
|
|||||||
@ -20,15 +20,19 @@ namespace Artemis.Core
|
|||||||
#region Constructors
|
#region Constructors
|
||||||
|
|
||||||
internal OutputPinCollection(INode node, string name, int initialCount)
|
internal OutputPinCollection(INode node, string name, int initialCount)
|
||||||
: base(node, name, initialCount)
|
: base(node, name)
|
||||||
{ }
|
{
|
||||||
|
// Can't do this in the base constructor because the type won't be set yet
|
||||||
|
for (int i = 0; i < initialCount; i++)
|
||||||
|
Add(CreatePin());
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Methods
|
#region Methods
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override IPin CreatePin() => new OutputPin<T>(Node, string.Empty);
|
public override IPin CreatePin() => new OutputPin<T>(Node, string.Empty);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,15 +16,11 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="node">The node the pin collection belongs to</param>
|
/// <param name="node">The node the pin collection belongs to</param>
|
||||||
/// <param name="name">The name of the pin collection</param>
|
/// <param name="name">The name of the pin collection</param>
|
||||||
/// <param name="initialCount">The amount of pins to initially add to the collection</param>
|
|
||||||
/// <returns>The resulting output pin collection</returns>
|
/// <returns>The resulting output pin collection</returns>
|
||||||
protected PinCollection(INode node, string name, int initialCount)
|
protected PinCollection(INode node, string name)
|
||||||
{
|
{
|
||||||
Node = node ?? throw new ArgumentNullException(nameof(node));
|
Node = node ?? throw new ArgumentNullException(nameof(node));
|
||||||
Name = name;
|
Name = name;
|
||||||
|
|
||||||
for (int i = 0; i < initialCount; i++)
|
|
||||||
AddPin();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -61,14 +57,18 @@ namespace Artemis.Core
|
|||||||
#region Methods
|
#region Methods
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IPin AddPin()
|
public void Add(IPin pin)
|
||||||
{
|
{
|
||||||
IPin pin = CreatePin();
|
if (pin.Direction != Direction)
|
||||||
|
throw new ArtemisCoreException($"Can't add a {pin.Direction} pin to an {Direction} pin collection.");
|
||||||
|
if (pin.Type != Type)
|
||||||
|
throw new ArtemisCoreException($"Can't add a {pin.Type} pin to an {Type} pin collection.");
|
||||||
|
|
||||||
|
if (_pins.Contains(pin))
|
||||||
|
return;
|
||||||
|
|
||||||
_pins.Add(pin);
|
_pins.Add(pin);
|
||||||
|
|
||||||
PinAdded?.Invoke(this, new SingleValueEventArgs<IPin>(pin));
|
PinAdded?.Invoke(this, new SingleValueEventArgs<IPin>(pin));
|
||||||
|
|
||||||
return pin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -89,11 +89,8 @@ namespace Artemis.Core
|
|||||||
pin.Reset();
|
pin.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Creates a new pin to be used in this collection
|
public abstract IPin CreatePin();
|
||||||
/// </summary>
|
|
||||||
/// <returns>The resulting pin</returns>
|
|
||||||
protected abstract IPin CreatePin();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IEnumerator<IPin> GetEnumerator()
|
public IEnumerator<IPin> GetEnumerator()
|
||||||
|
|||||||
@ -70,7 +70,7 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper
|
|||||||
Node.Script.OnScriptUpdated();
|
Node.Script.OnScriptUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddPin() => PinCollection.AddPin();
|
public void AddPin() => PinCollection.Add(PinCollection.CreatePin());
|
||||||
|
|
||||||
public void RemovePin(VisualScriptPin pin) => PinCollection.Remove(pin.Pin);
|
public void RemovePin(VisualScriptPin pin) => PinCollection.Remove(pin.Pin);
|
||||||
|
|
||||||
|
|||||||
@ -62,6 +62,7 @@ public class SelectionRectangle : Control
|
|||||||
private bool _isSelecting;
|
private bool _isSelecting;
|
||||||
private IControl? _oldInputElement;
|
private IControl? _oldInputElement;
|
||||||
private Point _startPosition;
|
private Point _startPosition;
|
||||||
|
private Point _lastPosition;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public SelectionRectangle()
|
public SelectionRectangle()
|
||||||
@ -168,6 +169,13 @@ public class SelectionRectangle : Control
|
|||||||
|
|
||||||
private void ParentOnPointerMoved(object? sender, PointerEventArgs e)
|
private void ParentOnPointerMoved(object? sender, PointerEventArgs e)
|
||||||
{
|
{
|
||||||
|
// Point moved seems to trigger when the element under the mouse changes?
|
||||||
|
// I'm not sure why this is needed but this check makes sure the position really hasn't changed.
|
||||||
|
Point position = e.GetCurrentPoint(null).Position;
|
||||||
|
if (position == _lastPosition)
|
||||||
|
return;
|
||||||
|
_lastPosition = position;
|
||||||
|
|
||||||
if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
|
if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,7 @@ public class AddNode : INodeEditorCommand, IDisposable
|
|||||||
private bool _isRemoved;
|
private bool _isRemoved;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the <see cref="MoveNode" /> class.
|
/// Creates a new instance of the <see cref="AddNode" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="nodeScript">The node script the node belongs to.</param>
|
/// <param name="nodeScript">The node script the node belongs to.</param>
|
||||||
/// <param name="node">The node to delete.</param>
|
/// <param name="node">The node to delete.</param>
|
||||||
|
|||||||
@ -0,0 +1,38 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Shared.Services.NodeEditor.Commands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a node editor command that can be used to add a pin to a pin collection.
|
||||||
|
/// </summary>
|
||||||
|
public class AddPin : INodeEditorCommand
|
||||||
|
{
|
||||||
|
private readonly IPinCollection _pinCollection;
|
||||||
|
private IPin? _pin;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="AddPin" /> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pinCollection">The pin collection to add the pin to.</param>
|
||||||
|
public AddPin(IPinCollection pinCollection)
|
||||||
|
{
|
||||||
|
_pinCollection = pinCollection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string DisplayName => "Add pin";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Execute()
|
||||||
|
{
|
||||||
|
_pin ??= _pinCollection.CreatePin();
|
||||||
|
_pinCollection.Add(_pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Undo()
|
||||||
|
{
|
||||||
|
if (_pin != null)
|
||||||
|
_pinCollection.Remove(_pin);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,14 +1,22 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.UI.Shared.Services.NodeEditor;
|
|
||||||
|
|
||||||
|
namespace Artemis.UI.Shared.Services.NodeEditor.Commands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a node editor command that can be used to connect two pins.
|
||||||
|
/// </summary>
|
||||||
public class ConnectPins : INodeEditorCommand
|
public class ConnectPins : INodeEditorCommand
|
||||||
{
|
{
|
||||||
private readonly IPin _source;
|
private readonly IPin _source;
|
||||||
private readonly IPin _target;
|
private readonly IPin _target;
|
||||||
private readonly List<IPin>? _originalConnections;
|
private readonly List<IPin>? _originalConnections;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="ConnectPins" /> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">The source of the connection.</param>
|
||||||
|
/// <param name="target">The target of the connection.</param>
|
||||||
public ConnectPins(IPin source, IPin target)
|
public ConnectPins(IPin source, IPin target)
|
||||||
{
|
{
|
||||||
_source = source;
|
_source = source;
|
||||||
|
|||||||
@ -11,11 +11,11 @@ public class DeleteNode : INodeEditorCommand, IDisposable
|
|||||||
{
|
{
|
||||||
private readonly INode _node;
|
private readonly INode _node;
|
||||||
private readonly INodeScript _nodeScript;
|
private readonly INodeScript _nodeScript;
|
||||||
private readonly Dictionary<IPin, IReadOnlyList<IPin>> _pinConnections = new();
|
private readonly Dictionary<IPin, List<IPin>> _pinConnections = new();
|
||||||
private bool _isRemoved;
|
private bool _isRemoved;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the <see cref="MoveNode" /> class.
|
/// Creates a new instance of the <see cref="DeleteNode" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="nodeScript">The node script the node belongs to.</param>
|
/// <param name="nodeScript">The node script the node belongs to.</param>
|
||||||
/// <param name="node">The node to delete.</param>
|
/// <param name="node">The node to delete.</param>
|
||||||
@ -30,7 +30,7 @@ public class DeleteNode : INodeEditorCommand, IDisposable
|
|||||||
_pinConnections.Clear();
|
_pinConnections.Clear();
|
||||||
foreach (IPin nodePin in _node.Pins)
|
foreach (IPin nodePin in _node.Pins)
|
||||||
{
|
{
|
||||||
_pinConnections.Add(nodePin, nodePin.ConnectedTo);
|
_pinConnections.Add(nodePin, new List<IPin>(nodePin.ConnectedTo));
|
||||||
nodePin.DisconnectAll();
|
nodePin.DisconnectAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ public class DeleteNode : INodeEditorCommand, IDisposable
|
|||||||
{
|
{
|
||||||
foreach (IPin nodePin in nodePinCollection)
|
foreach (IPin nodePin in nodePinCollection)
|
||||||
{
|
{
|
||||||
_pinConnections.Add(nodePin, nodePin.ConnectedTo);
|
_pinConnections.Add(nodePin, new List<IPin>(nodePin.ConnectedTo));
|
||||||
nodePin.DisconnectAll();
|
nodePin.DisconnectAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -48,18 +48,22 @@ public class DeleteNode : INodeEditorCommand, IDisposable
|
|||||||
{
|
{
|
||||||
foreach (IPin nodePin in _node.Pins)
|
foreach (IPin nodePin in _node.Pins)
|
||||||
{
|
{
|
||||||
if (_pinConnections.TryGetValue(nodePin, out IReadOnlyList<IPin>? connections))
|
if (_pinConnections.TryGetValue(nodePin, out List<IPin>? connections))
|
||||||
|
{
|
||||||
foreach (IPin connection in connections)
|
foreach (IPin connection in connections)
|
||||||
nodePin.ConnectTo(connection);
|
nodePin.ConnectTo(connection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (IPinCollection nodePinCollection in _node.PinCollections)
|
foreach (IPinCollection nodePinCollection in _node.PinCollections)
|
||||||
{
|
{
|
||||||
foreach (IPin nodePin in nodePinCollection)
|
foreach (IPin nodePin in nodePinCollection)
|
||||||
{
|
{
|
||||||
if (_pinConnections.TryGetValue(nodePin, out IReadOnlyList<IPin>? connections))
|
if (_pinConnections.TryGetValue(nodePin, out List<IPin>? connections))
|
||||||
|
{
|
||||||
foreach (IPin connection in connections)
|
foreach (IPin connection in connections)
|
||||||
nodePin.ConnectTo(connection);
|
nodePin.ConnectTo(connection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,49 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Artemis.Core;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Shared.Services.NodeEditor.Commands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a node editor command that can be used to remove a pin from a pin collection.
|
||||||
|
/// </summary>
|
||||||
|
public class RemovePin : INodeEditorCommand
|
||||||
|
{
|
||||||
|
private readonly IPinCollection _pinCollection;
|
||||||
|
private readonly IPin _pin;
|
||||||
|
private readonly List<IPin> _originalConnections;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="RemovePin" /> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pinCollection">The pin collection to add the pin to.</param>
|
||||||
|
/// <param name="pin">The pin to remove.</param>
|
||||||
|
public RemovePin(IPinCollection pinCollection, IPin pin)
|
||||||
|
{
|
||||||
|
if (!pinCollection.Contains(pin))
|
||||||
|
throw new ArtemisSharedUIException("Can't remove a pin from a collection it isn't contained in.");
|
||||||
|
|
||||||
|
_pinCollection = pinCollection;
|
||||||
|
_pin = pin;
|
||||||
|
|
||||||
|
_originalConnections = new List<IPin>(_pin.ConnectedTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string DisplayName => "Remove pin";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Execute()
|
||||||
|
{
|
||||||
|
_pin.DisconnectAll();
|
||||||
|
_pinCollection.Remove(_pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Undo()
|
||||||
|
{
|
||||||
|
_pinCollection.Add(_pin);
|
||||||
|
foreach (IPin originalConnection in _originalConnections)
|
||||||
|
_pin.ConnectTo(originalConnection);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -59,6 +59,19 @@
|
|||||||
<Setter Property="BorderBrush" Value="Transparent" />
|
<Setter Property="BorderBrush" Value="Transparent" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector=":is(Button).icon-button:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource AppBarButtonBackgroundPointerOver}" />
|
||||||
|
<Setter Property="TextBlock.Foreground" Value="{DynamicResource AppBarButtonForegroundPointerOver}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector=":is(Button).icon-button:pressed /template/ ContentPresenter#PART_ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource AppBarButtonBackgroundPressed}" />
|
||||||
|
<Setter Property="TextBlock.Foreground" Value="{DynamicResource AppBarButtonForegroundPressed}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector=":is(Button).icon-button:disabled /template/ ContentPresenter#PART_ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource AppBarButtonBackgroundDisabled}" />
|
||||||
|
<Setter Property="TextBlock.Foreground" Value="{DynamicResource AppBarButtonForegroundDisabled}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
<Style Selector=":is(Button).icon-button-small">
|
<Style Selector=":is(Button).icon-button-small">
|
||||||
<Setter Property="Padding" Value="4" />
|
<Setter Property="Padding" Value="4" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|||||||
@ -101,9 +101,9 @@ namespace Artemis.UI.Ninject.Factories
|
|||||||
|
|
||||||
public interface INodePinVmFactory
|
public interface INodePinVmFactory
|
||||||
{
|
{
|
||||||
PinCollectionViewModel InputPinCollectionViewModel(IPinCollection inputPinCollection);
|
PinCollectionViewModel InputPinCollectionViewModel(IPinCollection inputPinCollection, NodeScriptViewModel nodeScriptViewModel);
|
||||||
PinViewModel InputPinViewModel(IPin inputPin);
|
PinViewModel InputPinViewModel(IPin inputPin);
|
||||||
PinCollectionViewModel OutputPinCollectionViewModel(IPinCollection outputPinCollection);
|
PinCollectionViewModel OutputPinCollectionViewModel(IPinCollection outputPinCollection, NodeScriptViewModel nodeScriptViewModel);
|
||||||
PinViewModel OutputPinViewModel(IPin outputPin);
|
PinViewModel OutputPinViewModel(IPin outputPin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4,32 +4,78 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:visualScripting="clr-namespace:Artemis.UI.Screens.VisualScripting"
|
xmlns:visualScripting="clr-namespace:Artemis.UI.Screens.VisualScripting"
|
||||||
xmlns:converters="clr-namespace:Artemis.UI.Converters"
|
xmlns:converters="clr-namespace:Artemis.UI.Converters"
|
||||||
|
xmlns:skiaSharp="clr-namespace:SkiaSharp;assembly=SkiaSharp"
|
||||||
|
xmlns:shared="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.Screens.VisualScripting.CableView"
|
x:Class="Artemis.UI.Screens.VisualScripting.CableView"
|
||||||
x:DataType="visualScripting:CableViewModel"
|
x:DataType="visualScripting:CableViewModel"
|
||||||
ClipToBounds="False">
|
ClipToBounds="False"
|
||||||
|
IsVisible="{CompiledBinding Connected}">
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<converters:ColorToSolidColorBrushConverter x:Key="ColorToSolidColorBrushConverter" />
|
<converters:ColorToSolidColorBrushConverter x:Key="ColorToSolidColorBrushConverter" />
|
||||||
|
<shared:SKColorToStringConverter x:Key="SKColorToStringConverter" />
|
||||||
|
<shared:SKColorToColorConverter x:Key="SKColorToColorConverter" />
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
<Path Name="CablePath"
|
<Canvas>
|
||||||
Stroke="{CompiledBinding CableColor, Converter={StaticResource ColorToSolidColorBrushConverter}}"
|
<Path Name="CablePath"
|
||||||
StrokeThickness="4"
|
Stroke="{CompiledBinding CableColor, Converter={StaticResource ColorToSolidColorBrushConverter}}"
|
||||||
StrokeLineCap="Round">
|
StrokeThickness="4"
|
||||||
<Path.Transitions>
|
StrokeLineCap="Round">
|
||||||
<Transitions>
|
<Path.Transitions>
|
||||||
<ThicknessTransition Property="Margin" Duration="200"></ThicknessTransition>
|
<Transitions>
|
||||||
</Transitions>
|
<ThicknessTransition Property="Margin" Duration="200"></ThicknessTransition>
|
||||||
</Path.Transitions>
|
</Transitions>
|
||||||
<Path.Data>
|
</Path.Transitions>
|
||||||
<PathGeometry>
|
<Path.Data>
|
||||||
<PathGeometry.Figures>
|
<PathGeometry>
|
||||||
<PathFigure IsClosed="False">
|
<PathGeometry.Figures>
|
||||||
<PathFigure.Segments>
|
<PathFigure IsClosed="False">
|
||||||
<BezierSegment />
|
<PathFigure.Segments>
|
||||||
</PathFigure.Segments>
|
<BezierSegment />
|
||||||
</PathFigure>
|
</PathFigure.Segments>
|
||||||
</PathGeometry.Figures>
|
</PathFigure>
|
||||||
</PathGeometry>
|
</PathGeometry.Figures>
|
||||||
</Path.Data>
|
</PathGeometry>
|
||||||
</Path>
|
</Path.Data>
|
||||||
|
</Path>
|
||||||
|
<Border Name="ValueBorder"
|
||||||
|
Background="{DynamicResource ContentDialogBackground}"
|
||||||
|
BorderBrush="{CompiledBinding CableColor, Converter={StaticResource ColorToSolidColorBrushConverter}}"
|
||||||
|
BorderThickness="2"
|
||||||
|
CornerRadius="3"
|
||||||
|
Padding="4"
|
||||||
|
Canvas.Left="{CompiledBinding ValuePoint.X}"
|
||||||
|
Canvas.Top="{CompiledBinding ValuePoint.Y}">
|
||||||
|
<ContentControl Content="{CompiledBinding FromViewModel.Pin.PinValue}">
|
||||||
|
<ContentControl.DataTemplates>
|
||||||
|
<DataTemplate DataType="skiaSharp:SKColor">
|
||||||
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||||
|
<TextBlock x:Name="HexDisplay"
|
||||||
|
Text="{CompiledBinding Converter={StaticResource SKColorToStringConverter}}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
HorizontalAlignment="Stretch" />
|
||||||
|
<Border Margin="5 0 0 0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
BorderThickness="1"
|
||||||
|
MinWidth="18"
|
||||||
|
MinHeight="18"
|
||||||
|
Background="{DynamicResource CheckerboardBrush}"
|
||||||
|
BorderBrush="{DynamicResource ColorPickerButtonOutline}"
|
||||||
|
CornerRadius="2">
|
||||||
|
<Ellipse Stroke="{DynamicResource NormalBorderBrush}">
|
||||||
|
<Ellipse.Fill>
|
||||||
|
<SolidColorBrush Color="{Binding Converter={StaticResource SKColorToColorConverter}}" />
|
||||||
|
</Ellipse.Fill>
|
||||||
|
</Ellipse>
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<TextBlock Text="{Binding}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ContentControl.DataTemplates>
|
||||||
|
</ContentControl>
|
||||||
|
</Border>
|
||||||
|
</Canvas>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -15,15 +15,19 @@ public class CableView : ReactiveUserControl<CableViewModel>
|
|||||||
{
|
{
|
||||||
private const double CABLE_OFFSET = 24 * 4;
|
private const double CABLE_OFFSET = 24 * 4;
|
||||||
private readonly Path _cablePath;
|
private readonly Path _cablePath;
|
||||||
|
private readonly Border _valueBorder;
|
||||||
|
|
||||||
public CableView()
|
public CableView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_cablePath = this.Get<Path>("CablePath");
|
_cablePath = this.Get<Path>("CablePath");
|
||||||
|
_valueBorder = this.Get<Border>("ValueBorder");
|
||||||
|
|
||||||
// Not using bindings here to avoid a warnings
|
// Not using bindings here to avoid a warnings
|
||||||
this.WhenActivated(d =>
|
this.WhenActivated(d =>
|
||||||
{
|
{
|
||||||
|
_valueBorder.GetObservable(BoundsProperty).Subscribe(rect => _valueBorder.RenderTransform = new TranslateTransform(rect.Width / 2 * -1, rect.Height / 2 * -1)).DisposeWith(d);
|
||||||
|
|
||||||
ViewModel.WhenAnyValue(vm => vm.FromPoint).Subscribe(_ => Update()).DisposeWith(d);
|
ViewModel.WhenAnyValue(vm => vm.FromPoint).Subscribe(_ => Update()).DisposeWith(d);
|
||||||
ViewModel.WhenAnyValue(vm => vm.ToPoint).Subscribe(_ => Update()).DisposeWith(d);
|
ViewModel.WhenAnyValue(vm => vm.ToPoint).Subscribe(_ => Update()).DisposeWith(d);
|
||||||
Update();
|
Update();
|
||||||
|
|||||||
@ -15,9 +15,11 @@ namespace Artemis.UI.Screens.VisualScripting;
|
|||||||
|
|
||||||
public class CableViewModel : ActivatableViewModelBase
|
public class CableViewModel : ActivatableViewModelBase
|
||||||
{
|
{
|
||||||
|
private readonly ObservableAsPropertyHelper<bool> _connected;
|
||||||
private readonly ObservableAsPropertyHelper<Color> _cableColor;
|
private readonly ObservableAsPropertyHelper<Color> _cableColor;
|
||||||
private readonly ObservableAsPropertyHelper<Point> _fromPoint;
|
private readonly ObservableAsPropertyHelper<Point> _fromPoint;
|
||||||
private readonly ObservableAsPropertyHelper<Point> _toPoint;
|
private readonly ObservableAsPropertyHelper<Point> _toPoint;
|
||||||
|
private readonly ObservableAsPropertyHelper<Point> _valuePoint;
|
||||||
|
|
||||||
private PinViewModel? _fromViewModel;
|
private PinViewModel? _fromViewModel;
|
||||||
private PinViewModel? _toViewModel;
|
private PinViewModel? _toViewModel;
|
||||||
@ -44,10 +46,19 @@ public class CableViewModel : ActivatableViewModelBase
|
|||||||
.Select(p => p != null ? p.WhenAnyValue(pvm => pvm.Position) : Observable.Never<Point>())
|
.Select(p => p != null ? p.WhenAnyValue(pvm => pvm.Position) : Observable.Never<Point>())
|
||||||
.Switch()
|
.Switch()
|
||||||
.ToProperty(this, vm => vm.ToPoint);
|
.ToProperty(this, vm => vm.ToPoint);
|
||||||
|
_valuePoint = this.WhenAnyValue(vm => vm.FromPoint, vm => vm.ToPoint).Select(tuple => new Point(
|
||||||
|
tuple.Item1.X + (tuple.Item2.X - tuple.Item1.X) / 2,
|
||||||
|
tuple.Item1.Y + (tuple.Item2.Y - tuple.Item1.Y) / 2
|
||||||
|
)).ToProperty(this, vm => vm.ValuePoint);
|
||||||
|
|
||||||
_cableColor = this.WhenAnyValue(vm => vm.FromViewModel, vm => vm.ToViewModel)
|
_cableColor = this.WhenAnyValue(vm => vm.FromViewModel, vm => vm.ToViewModel)
|
||||||
.Select(tuple => tuple.Item1?.PinColor ?? tuple.Item2?.PinColor ?? new Color(255, 255, 255, 255))
|
.Select(tuple => tuple.Item1?.PinColor ?? tuple.Item2?.PinColor ?? new Color(255, 255, 255, 255))
|
||||||
.ToProperty(this, vm => vm.CableColor);
|
.ToProperty(this, vm => vm.CableColor);
|
||||||
|
|
||||||
|
// Not a perfect solution but this makes sure the cable never renders at 0,0 (can happen when the cable spawns before the pin ever rendered)
|
||||||
|
_connected = this.WhenAnyValue(vm => vm.FromPoint, vm => vm.ToPoint)
|
||||||
|
.Select(tuple => tuple.Item1 != new Point(0, 0) && tuple.Item2 != new Point(0, 0))
|
||||||
|
.ToProperty(this, vm => vm.Connected);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PinViewModel? FromViewModel
|
public PinViewModel? FromViewModel
|
||||||
@ -62,7 +73,10 @@ public class CableViewModel : ActivatableViewModelBase
|
|||||||
set => RaiseAndSetIfChanged(ref _toViewModel, value);
|
set => RaiseAndSetIfChanged(ref _toViewModel, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Connected => _connected.Value;
|
||||||
|
|
||||||
public Point FromPoint => _fromPoint.Value;
|
public Point FromPoint => _fromPoint.Value;
|
||||||
public Point ToPoint => _toPoint.Value;
|
public Point ToPoint => _toPoint.Value;
|
||||||
|
public Point ValuePoint => _valuePoint.Value;
|
||||||
public Color CableColor => _cableColor.Value;
|
public Color CableColor => _cableColor.Value;
|
||||||
}
|
}
|
||||||
@ -10,13 +10,15 @@
|
|||||||
<UserControl.Styles>
|
<UserControl.Styles>
|
||||||
<Style Selector="Border.node-container">
|
<Style Selector="Border.node-container">
|
||||||
<Setter Property="CornerRadius" Value="6" />
|
<Setter Property="CornerRadius" Value="6" />
|
||||||
<Setter Property="Background" Value="{DynamicResource ContentDialogBackground}" />
|
<Setter Property="Background">
|
||||||
|
<SolidColorBrush Color="{DynamicResource SolidBackgroundFillColorBase}" Opacity="0.75"></SolidColorBrush>
|
||||||
|
</Setter>
|
||||||
<Setter Property="BorderBrush" Value="{DynamicResource CardStrokeColorDefaultBrush}" />
|
<Setter Property="BorderBrush" Value="{DynamicResource CardStrokeColorDefaultBrush}" />
|
||||||
<Setter Property="BorderThickness" Value="1" />
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
<Setter Property="Transitions">
|
<Setter Property="Transitions">
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<Transitions>
|
<Transitions>
|
||||||
<BrushTransition Property="BorderBrush" Duration="0:0:0.2" Easing="CubicEaseOut"/>
|
<BrushTransition Property="BorderBrush" Duration="0:0:0.2" Easing="CubicEaseOut" />
|
||||||
</Transitions>
|
</Transitions>
|
||||||
</Setter.Value>
|
</Setter.Value>
|
||||||
</Setter>
|
</Setter>
|
||||||
@ -24,46 +26,50 @@
|
|||||||
<Style Selector="Border.node-container-selected">
|
<Style Selector="Border.node-container-selected">
|
||||||
<Setter Property="BorderBrush" Value="{DynamicResource SystemAccentColor}" />
|
<Setter Property="BorderBrush" Value="{DynamicResource SystemAccentColor}" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style Selector="ContentControl#CustomViewModelContainer">
|
<Style Selector="ContentControl#CustomViewModelContainer">
|
||||||
<Setter Property="Margin" Value="20 0"></Setter>
|
<Setter Property="Margin" Value="20 0"></Setter>
|
||||||
</Style>
|
</Style>
|
||||||
</UserControl.Styles>
|
</UserControl.Styles>
|
||||||
<Border Classes="node-container" Classes.node-container-selected="{CompiledBinding IsSelected}">
|
<Border Classes="node-container" Classes.node-container-selected="{CompiledBinding IsSelected}">
|
||||||
<Grid RowDefinitions="Auto,*">
|
<Grid RowDefinitions="Auto,*">
|
||||||
<Border Grid.Row="0"
|
<Border Grid.Row="0"
|
||||||
Background="{DynamicResource TaskDialogHeaderBackground}"
|
|
||||||
CornerRadius="6 6 0 0"
|
CornerRadius="6 6 0 0"
|
||||||
Cursor="Hand"
|
Cursor="Hand"
|
||||||
PointerReleased="InputElement_OnPointerReleased"
|
PointerReleased="InputElement_OnPointerReleased"
|
||||||
PointerMoved="InputElement_OnPointerMoved">
|
PointerMoved="InputElement_OnPointerMoved"
|
||||||
<Grid Classes="node-header"
|
ClipToBounds="True"
|
||||||
VerticalAlignment="Top"
|
Background="{DynamicResource ContentDialogBackground}">
|
||||||
ColumnDefinitions="*,Auto">
|
<Border Background="{DynamicResource TaskDialogHeaderBackground}">
|
||||||
<TextBlock VerticalAlignment="Center"
|
<Grid Classes="node-header"
|
||||||
TextAlignment="Center"
|
VerticalAlignment="Top"
|
||||||
Margin="5"
|
ColumnDefinitions="*,Auto">
|
||||||
Text="{CompiledBinding Node.Name}"
|
<TextBlock VerticalAlignment="Center"
|
||||||
ToolTip.Tip="{CompiledBinding Node.Description}">
|
TextAlignment="Center"
|
||||||
</TextBlock>
|
Margin="5"
|
||||||
<Button VerticalAlignment="Center"
|
Text="{CompiledBinding Node.Name}"
|
||||||
Classes="icon-button icon-button-small"
|
ToolTip.Tip="{CompiledBinding Node.Description}">
|
||||||
Grid.Column="1"
|
</TextBlock>
|
||||||
Margin="5"
|
<Button VerticalAlignment="Center"
|
||||||
Command="{CompiledBinding DeleteNode}">
|
Classes="icon-button icon-button-small"
|
||||||
<avalonia:MaterialIcon Kind="Close"></avalonia:MaterialIcon>
|
Grid.Column="1"
|
||||||
</Button>
|
Margin="5"
|
||||||
</Grid>
|
Command="{CompiledBinding DeleteNode}">
|
||||||
|
<avalonia:MaterialIcon Kind="Close"></avalonia:MaterialIcon>
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
|
|
||||||
<Grid Grid.Row="1" ColumnDefinitions="Auto,*,Auto" Margin="5">
|
<Grid Grid.Row="1" ColumnDefinitions="Auto,*,Auto" Margin="5">
|
||||||
<StackPanel Grid.Column="0">
|
<StackPanel Grid.Column="0" IsVisible="{CompiledBinding HasInputPins}">
|
||||||
<ItemsControl Items="{CompiledBinding InputPinViewModels}" Margin="4 0" />
|
<ItemsControl Items="{CompiledBinding InputPinViewModels}" Margin="4 0" />
|
||||||
<ItemsControl Items="{CompiledBinding InputPinCollectionViewModels}" />
|
<ItemsControl Items="{CompiledBinding InputPinCollectionViewModels}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<ContentControl Name="CustomViewModelContainer" Grid.Column="1" Content="{CompiledBinding CustomNodeViewModel}" IsVisible="{CompiledBinding CustomNodeViewModel}" />
|
<ContentControl Grid.Column="1" Name="CustomViewModelContainer" Content="{CompiledBinding CustomNodeViewModel}" IsVisible="{CompiledBinding CustomNodeViewModel}" />
|
||||||
|
|
||||||
<StackPanel Grid.Column="2">
|
<StackPanel Grid.Column="2" IsVisible="{CompiledBinding HasInputPins}">
|
||||||
<ItemsControl Items="{CompiledBinding OutputPinViewModels}" Margin="4 0" />
|
<ItemsControl Items="{CompiledBinding OutputPinViewModels}" Margin="4 0" />
|
||||||
<ItemsControl Items="{CompiledBinding OutputPinCollectionViewModels}" />
|
<ItemsControl Items="{CompiledBinding OutputPinCollectionViewModels}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Reactive;
|
using System.Reactive;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
@ -25,10 +26,13 @@ public class NodeViewModel : ActivatableViewModelBase
|
|||||||
private double _dragOffsetX;
|
private double _dragOffsetX;
|
||||||
private double _dragOffsetY;
|
private double _dragOffsetY;
|
||||||
private bool _isSelected;
|
private bool _isSelected;
|
||||||
private ObservableAsPropertyHelper<bool>? _isStaticNode;
|
|
||||||
private double _startX;
|
private double _startX;
|
||||||
private double _startY;
|
private double _startY;
|
||||||
|
|
||||||
|
private ObservableAsPropertyHelper<bool>? _isStaticNode;
|
||||||
|
private ObservableAsPropertyHelper<bool>? _hasInputPins;
|
||||||
|
private ObservableAsPropertyHelper<bool>? _hasOutputPins;
|
||||||
|
|
||||||
public NodeViewModel(NodeScriptViewModel nodeScriptViewModel, INode node, INodePinVmFactory nodePinVmFactory, INodeEditorService nodeEditorService)
|
public NodeViewModel(NodeScriptViewModel nodeScriptViewModel, INode node, INodePinVmFactory nodePinVmFactory, INodeEditorService nodeEditorService)
|
||||||
{
|
{
|
||||||
NodeScriptViewModel = nodeScriptViewModel;
|
NodeScriptViewModel = nodeScriptViewModel;
|
||||||
@ -57,12 +61,12 @@ public class NodeViewModel : ActivatableViewModelBase
|
|||||||
// Same again but for pin collections
|
// Same again but for pin collections
|
||||||
nodePinCollections.Connect()
|
nodePinCollections.Connect()
|
||||||
.Filter(n => n.Direction == PinDirection.Input)
|
.Filter(n => n.Direction == PinDirection.Input)
|
||||||
.Transform(nodePinVmFactory.InputPinCollectionViewModel)
|
.Transform(c => nodePinVmFactory.InputPinCollectionViewModel(c, nodeScriptViewModel))
|
||||||
.Bind(out ReadOnlyObservableCollection<PinCollectionViewModel> inputPinCollections)
|
.Bind(out ReadOnlyObservableCollection<PinCollectionViewModel> inputPinCollections)
|
||||||
.Subscribe();
|
.Subscribe();
|
||||||
nodePinCollections.Connect()
|
nodePinCollections.Connect()
|
||||||
.Filter(n => n.Direction == PinDirection.Output)
|
.Filter(n => n.Direction == PinDirection.Output)
|
||||||
.Transform(nodePinVmFactory.OutputPinCollectionViewModel)
|
.Transform(c => nodePinVmFactory.OutputPinCollectionViewModel(c, nodeScriptViewModel))
|
||||||
.Bind(out ReadOnlyObservableCollection<PinCollectionViewModel> outputPinCollections)
|
.Bind(out ReadOnlyObservableCollection<PinCollectionViewModel> outputPinCollections)
|
||||||
.Subscribe();
|
.Subscribe();
|
||||||
InputPinCollectionViewModels = inputPinCollections;
|
InputPinCollectionViewModels = inputPinCollections;
|
||||||
@ -84,6 +88,16 @@ public class NodeViewModel : ActivatableViewModelBase
|
|||||||
.Select(tuple => tuple.Item1 || tuple.Item2)
|
.Select(tuple => tuple.Item1 || tuple.Item2)
|
||||||
.ToProperty(this, model => model.IsStaticNode)
|
.ToProperty(this, model => model.IsStaticNode)
|
||||||
.DisposeWith(d);
|
.DisposeWith(d);
|
||||||
|
_hasInputPins = InputPinViewModels.ToObservableChangeSet()
|
||||||
|
.Merge(InputPinCollectionViewModels.ToObservableChangeSet().TransformMany(c => c.PinViewModels))
|
||||||
|
.Any()
|
||||||
|
.ToProperty(this, vm => vm.HasInputPins)
|
||||||
|
.DisposeWith(d);
|
||||||
|
_hasOutputPins = OutputPinViewModels.ToObservableChangeSet()
|
||||||
|
.Merge(OutputPinCollectionViewModels.ToObservableChangeSet().TransformMany(c => c.PinViewModels))
|
||||||
|
.Any()
|
||||||
|
.ToProperty(this, vm => vm.HasOutputPins)
|
||||||
|
.DisposeWith(d);
|
||||||
|
|
||||||
// Subscribe to pin changes
|
// Subscribe to pin changes
|
||||||
Node.WhenAnyValue(n => n.Pins).Subscribe(p => nodePins.Edit(source =>
|
Node.WhenAnyValue(n => n.Pins).Subscribe(p => nodePins.Edit(source =>
|
||||||
@ -97,10 +111,15 @@ public class NodeViewModel : ActivatableViewModelBase
|
|||||||
source.Clear();
|
source.Clear();
|
||||||
source.AddRange(c);
|
source.AddRange(c);
|
||||||
})).DisposeWith(d);
|
})).DisposeWith(d);
|
||||||
|
|
||||||
|
if (Node is Node coreNode)
|
||||||
|
CustomNodeViewModel = coreNode.GetCustomViewModel();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsStaticNode => _isStaticNode?.Value ?? true;
|
public bool IsStaticNode => _isStaticNode?.Value ?? true;
|
||||||
|
public bool HasInputPins => _hasInputPins?.Value ?? false;
|
||||||
|
public bool HasOutputPins => _hasOutputPins?.Value ?? false;
|
||||||
|
|
||||||
public NodeScriptViewModel NodeScriptViewModel { get; }
|
public NodeScriptViewModel NodeScriptViewModel { get; }
|
||||||
public INode Node { get; }
|
public INode Node { get; }
|
||||||
|
|||||||
@ -13,7 +13,20 @@
|
|||||||
Command="{CompiledBinding AddPin}">
|
Command="{CompiledBinding AddPin}">
|
||||||
<avalonia:MaterialIcon Kind="Add"></avalonia:MaterialIcon>
|
<avalonia:MaterialIcon Kind="Add"></avalonia:MaterialIcon>
|
||||||
</Button>
|
</Button>
|
||||||
<ItemsControl Items="{CompiledBinding PinViewModels}" Margin="4 0"></ItemsControl>
|
<ItemsControl Items="{CompiledBinding PinViewModels}" Margin="4 0">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate DataType="pins:PinViewModel">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<ContentControl Content="{CompiledBinding}"></ContentControl>
|
||||||
|
<Button Classes="icon-button icon-button-small"
|
||||||
|
ToolTip.Tip="Remove pin"
|
||||||
|
Command="{CompiledBinding RemovePin}"
|
||||||
|
CommandParameter="{CompiledBinding Pin}">
|
||||||
|
<avalonia:MaterialIcon Kind="Close"></avalonia:MaterialIcon>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.UI.Ninject.Factories;
|
using Artemis.UI.Ninject.Factories;
|
||||||
|
using Artemis.UI.Shared.Services.NodeEditor;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.VisualScripting.Pins;
|
namespace Artemis.UI.Screens.VisualScripting.Pins;
|
||||||
|
|
||||||
@ -7,7 +8,8 @@ public class InputPinCollectionViewModel<T> : PinCollectionViewModel
|
|||||||
{
|
{
|
||||||
public InputPinCollection<T> InputPinCollection { get; }
|
public InputPinCollection<T> InputPinCollection { get; }
|
||||||
|
|
||||||
public InputPinCollectionViewModel(InputPinCollection<T> inputPinCollection, INodePinVmFactory nodePinVmFactory) : base(inputPinCollection, nodePinVmFactory)
|
public InputPinCollectionViewModel(InputPinCollection<T> inputPinCollection, NodeScriptViewModel nodeScriptViewModel, INodePinVmFactory nodePinVmFactory, INodeEditorService nodeEditorService)
|
||||||
|
: base(inputPinCollection, nodeScriptViewModel, nodePinVmFactory, nodeEditorService)
|
||||||
{
|
{
|
||||||
InputPinCollection = inputPinCollection;
|
InputPinCollection = inputPinCollection;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,6 +14,20 @@
|
|||||||
Command="{CompiledBinding AddPin}">
|
Command="{CompiledBinding AddPin}">
|
||||||
<avalonia:MaterialIcon Kind="Add"></avalonia:MaterialIcon>
|
<avalonia:MaterialIcon Kind="Add"></avalonia:MaterialIcon>
|
||||||
</Button>
|
</Button>
|
||||||
<ItemsControl Items="{CompiledBinding PinViewModels}" HorizontalAlignment="Right" Margin="4 0"></ItemsControl>
|
<ItemsControl Items="{CompiledBinding PinViewModels}" Margin="4 0" HorizontalAlignment="Right">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate DataType="pins:PinViewModel">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Button Classes="icon-button icon-button-small"
|
||||||
|
ToolTip.Tip="Remove pin"
|
||||||
|
Command="{CompiledBinding RemovePin}"
|
||||||
|
CommandParameter="{CompiledBinding Pin}">
|
||||||
|
<avalonia:MaterialIcon Kind="Close"></avalonia:MaterialIcon>
|
||||||
|
</Button>
|
||||||
|
<ContentControl Content="{CompiledBinding}"></ContentControl>
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.UI.Ninject.Factories;
|
using Artemis.UI.Ninject.Factories;
|
||||||
|
using Artemis.UI.Shared.Services.NodeEditor;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.VisualScripting.Pins;
|
namespace Artemis.UI.Screens.VisualScripting.Pins;
|
||||||
|
|
||||||
@ -7,7 +8,8 @@ public class OutputPinCollectionViewModel<T> : PinCollectionViewModel
|
|||||||
{
|
{
|
||||||
public OutputPinCollection<T> OutputPinCollection { get; }
|
public OutputPinCollection<T> OutputPinCollection { get; }
|
||||||
|
|
||||||
public OutputPinCollectionViewModel(OutputPinCollection<T> outputPinCollection, INodePinVmFactory nodePinVmFactory) : base(outputPinCollection, nodePinVmFactory)
|
public OutputPinCollectionViewModel(OutputPinCollection<T> outputPinCollection, NodeScriptViewModel nodeScriptViewModel, INodePinVmFactory nodePinVmFactory, INodeEditorService nodeEditorService)
|
||||||
|
: base(outputPinCollection, nodeScriptViewModel, nodePinVmFactory, nodeEditorService)
|
||||||
{
|
{
|
||||||
OutputPinCollection = outputPinCollection;
|
OutputPinCollection = outputPinCollection;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,8 @@ using Artemis.Core;
|
|||||||
using Artemis.Core.Events;
|
using Artemis.Core.Events;
|
||||||
using Artemis.UI.Ninject.Factories;
|
using Artemis.UI.Ninject.Factories;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
|
using Artemis.UI.Shared.Services.NodeEditor;
|
||||||
|
using Artemis.UI.Shared.Services.NodeEditor.Commands;
|
||||||
using Avalonia.Controls.Mixins;
|
using Avalonia.Controls.Mixins;
|
||||||
using DynamicData;
|
using DynamicData;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
@ -16,10 +18,8 @@ namespace Artemis.UI.Screens.VisualScripting.Pins;
|
|||||||
public abstract class PinCollectionViewModel : ActivatableViewModelBase
|
public abstract class PinCollectionViewModel : ActivatableViewModelBase
|
||||||
{
|
{
|
||||||
private readonly INodePinVmFactory _nodePinVmFactory;
|
private readonly INodePinVmFactory _nodePinVmFactory;
|
||||||
public IPinCollection PinCollection { get; }
|
|
||||||
public ObservableCollection<PinViewModel> PinViewModels { get; }
|
|
||||||
|
|
||||||
protected PinCollectionViewModel(IPinCollection pinCollection, INodePinVmFactory nodePinVmFactory)
|
protected PinCollectionViewModel(IPinCollection pinCollection, NodeScriptViewModel nodeScriptViewModel, INodePinVmFactory nodePinVmFactory, INodeEditorService nodeEditorService)
|
||||||
{
|
{
|
||||||
_nodePinVmFactory = nodePinVmFactory;
|
_nodePinVmFactory = nodePinVmFactory;
|
||||||
|
|
||||||
@ -39,13 +39,20 @@ public abstract class PinCollectionViewModel : ActivatableViewModelBase
|
|||||||
.DisposeWith(d);
|
.DisposeWith(d);
|
||||||
});
|
});
|
||||||
|
|
||||||
AddPin = ReactiveCommand.Create(() => PinCollection.AddPin());
|
AddPin = ReactiveCommand.Create(() => nodeEditorService.ExecuteCommand(nodeScriptViewModel.NodeScript, new AddPin(pinCollection)));
|
||||||
|
RemovePin = ReactiveCommand.Create((IPin pin) => nodeEditorService.ExecuteCommand(nodeScriptViewModel.NodeScript, new RemovePin(pinCollection, pin)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReactiveCommand<Unit, IPin> AddPin { get; }
|
public IPinCollection PinCollection { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> AddPin { get; }
|
||||||
|
public ReactiveCommand<IPin, Unit> RemovePin { get; }
|
||||||
|
|
||||||
|
public ObservableCollection<PinViewModel> PinViewModels { get; }
|
||||||
|
|
||||||
private PinViewModel CreatePinViewModel(IPin pin)
|
private PinViewModel CreatePinViewModel(IPin pin)
|
||||||
{
|
{
|
||||||
return PinCollection.Direction == PinDirection.Input ? _nodePinVmFactory.InputPinViewModel(pin) : _nodePinVmFactory.OutputPinViewModel(pin);
|
PinViewModel vm = PinCollection.Direction == PinDirection.Input ? _nodePinVmFactory.InputPinViewModel(pin) : _nodePinVmFactory.OutputPinViewModel(pin);
|
||||||
|
vm.RemovePin = RemovePin;
|
||||||
|
return vm;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -78,12 +78,12 @@ public class PinView : ReactiveUserControl<PinViewModel>
|
|||||||
|
|
||||||
private void UpdatePosition()
|
private void UpdatePosition()
|
||||||
{
|
{
|
||||||
if (_container == null || ViewModel == null)
|
if (_container == null || _pinPoint == null || ViewModel == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Matrix? transform = this.TransformToVisual(_container);
|
Matrix? transform = _pinPoint.TransformToVisual(_container);
|
||||||
if (transform != null)
|
if (transform != null)
|
||||||
ViewModel.Position = new Point(Bounds.Width / 2, Bounds.Height / 2).Transform(transform.Value);
|
ViewModel.Position = new Point(_pinPoint.Bounds.Width / 2, _pinPoint.Bounds.Height / 2).Transform(transform.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Reactive;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.Core.Events;
|
using Artemis.Core.Events;
|
||||||
@ -16,6 +17,7 @@ namespace Artemis.UI.Screens.VisualScripting.Pins;
|
|||||||
public abstract class PinViewModel : ActivatableViewModelBase
|
public abstract class PinViewModel : ActivatableViewModelBase
|
||||||
{
|
{
|
||||||
private Point _position;
|
private Point _position;
|
||||||
|
private ReactiveCommand<IPin, Unit>? _removePin;
|
||||||
|
|
||||||
protected PinViewModel(IPin pin, INodeService nodeService)
|
protected PinViewModel(IPin pin, INodeService nodeService)
|
||||||
{
|
{
|
||||||
@ -52,6 +54,12 @@ public abstract class PinViewModel : ActivatableViewModelBase
|
|||||||
set => RaiseAndSetIfChanged(ref _position, value);
|
set => RaiseAndSetIfChanged(ref _position, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ReactiveCommand<IPin, Unit>? RemovePin
|
||||||
|
{
|
||||||
|
get => _removePin;
|
||||||
|
set => RaiseAndSetIfChanged(ref _removePin, value);
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsCompatibleWith(PinViewModel pinViewModel)
|
public bool IsCompatibleWith(PinViewModel pinViewModel)
|
||||||
{
|
{
|
||||||
if (pinViewModel.Pin.Direction == Pin.Direction)
|
if (pinViewModel.Pin.Direction == Pin.Direction)
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reactive;
|
using System.Reactive;
|
||||||
|
using System.Reactive.Disposables;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.UI.Ninject.Factories;
|
using Artemis.UI.Ninject.Factories;
|
||||||
@ -9,6 +10,7 @@ using Artemis.UI.Shared.Services.Builders;
|
|||||||
using Artemis.UI.Shared.Services.Interfaces;
|
using Artemis.UI.Shared.Services.Interfaces;
|
||||||
using Artemis.VisualScripting.Nodes;
|
using Artemis.VisualScripting.Nodes;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Threading;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
|
||||||
@ -16,6 +18,8 @@ namespace Artemis.UI.Screens.Workshop
|
|||||||
{
|
{
|
||||||
public class WorkshopViewModel : MainScreenViewModel
|
public class WorkshopViewModel : MainScreenViewModel
|
||||||
{
|
{
|
||||||
|
private static NodeScript<bool>? _testScript = null;
|
||||||
|
|
||||||
private readonly INotificationService _notificationService;
|
private readonly INotificationService _notificationService;
|
||||||
private StandardCursorType _selectedCursor;
|
private StandardCursorType _selectedCursor;
|
||||||
private readonly ObservableAsPropertyHelper<Cursor> _cursor;
|
private readonly ObservableAsPropertyHelper<Cursor> _cursor;
|
||||||
@ -38,16 +42,26 @@ namespace Artemis.UI.Screens.Workshop
|
|||||||
DisplayName = "Workshop";
|
DisplayName = "Workshop";
|
||||||
ShowNotification = ReactiveCommand.Create<NotificationSeverity>(ExecuteShowNotification);
|
ShowNotification = ReactiveCommand.Create<NotificationSeverity>(ExecuteShowNotification);
|
||||||
|
|
||||||
NodeScript<bool> testScript = new("Test script", "A test script");
|
if (_testScript == null)
|
||||||
INode exitNode = testScript.Nodes.Last();
|
{
|
||||||
exitNode.X = 300;
|
_testScript = new NodeScript<bool>("Test script", "A test script");
|
||||||
exitNode.Y = 150;
|
INode exitNode = _testScript.Nodes.Last();
|
||||||
|
exitNode.X = 300;
|
||||||
|
exitNode.Y = 150;
|
||||||
|
|
||||||
OrNode orNode = new() {X = 100, Y = 100};
|
OrNode orNode = new() {X = 100, Y = 100};
|
||||||
testScript.AddNode(orNode);
|
_testScript.AddNode(orNode);
|
||||||
orNode.Result.ConnectTo(exitNode.Pins.First());
|
orNode.Result.ConnectTo(exitNode.Pins.First());
|
||||||
|
}
|
||||||
|
|
||||||
VisualEditorViewModel = nodeVmFactory.NodeScriptViewModel(testScript);
|
VisualEditorViewModel = nodeVmFactory.NodeScriptViewModel(_testScript);
|
||||||
|
|
||||||
|
this.WhenActivated(d =>
|
||||||
|
{
|
||||||
|
DispatcherTimer updateTimer = new(TimeSpan.FromMilliseconds(20), DispatcherPriority.Normal, (_, _) => _testScript?.Run());
|
||||||
|
updateTimer.Start();
|
||||||
|
Disposable.Create(() => updateTimer.Stop()).DisposeWith(d);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public NodeScriptViewModel VisualEditorViewModel { get; }
|
public NodeScriptViewModel VisualEditorViewModel { get; }
|
||||||
|
|||||||
@ -0,0 +1,29 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using Artemis.Core;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
|
|
||||||
|
namespace Artemis.VisualScripting.Converters;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts input into <see cref="Numeric" />.
|
||||||
|
/// </summary>
|
||||||
|
public class NumericConverter : IValueConverter
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (targetType == typeof(Numeric))
|
||||||
|
return new Numeric(value);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (targetType == typeof(Numeric))
|
||||||
|
return new Numeric(value);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,25 +0,0 @@
|
|||||||
<UserControl x:Class="Artemis.VisualScripting.Nodes.CustomViews.EnumEqualsNodeCustomView"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
d:DesignHeight="450" d:DesignWidth="800">
|
|
||||||
<Grid>
|
|
||||||
<ComboBox Width="140"
|
|
||||||
materialDesign:ComboBoxAssist.ClassicMode="True"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
SelectedValue="{Binding Node.Storage}"
|
|
||||||
ItemsSource="{Binding EnumValues}"
|
|
||||||
SelectedValuePath="Value"
|
|
||||||
DisplayMemberPath="Description">
|
|
||||||
<ComboBox.ItemsPanel>
|
|
||||||
<ItemsPanelTemplate>
|
|
||||||
<VirtualizingStackPanel />
|
|
||||||
</ItemsPanelTemplate>
|
|
||||||
</ComboBox.ItemsPanel>
|
|
||||||
</ComboBox>
|
|
||||||
</Grid>
|
|
||||||
</UserControl>
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
<UserControl x:Class="Artemis.VisualScripting.Nodes.CustomViews.LayerPropertyNodeCustomView"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
d:DesignHeight="450" d:DesignWidth="800">
|
|
||||||
<StackPanel>
|
|
||||||
<ComboBox Margin="8 0"
|
|
||||||
ItemsSource="{Binding ProfileElements}"
|
|
||||||
SelectedValue="{Binding SelectedProfileElement}" />
|
|
||||||
<ComboBox Margin="8 0"
|
|
||||||
ItemsSource="{Binding LayerProperties}"
|
|
||||||
SelectedValue="{Binding SelectedLayerProperty}"
|
|
||||||
DisplayMemberPath="PropertyDescription.Name" />
|
|
||||||
</StackPanel>
|
|
||||||
</UserControl>
|
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:customViewModels="clr-namespace:Artemis.VisualScripting.Nodes.CustomViewModels"
|
||||||
|
xmlns:converters="clr-namespace:Artemis.VisualScripting.Converters"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.VisualScripting.Nodes.CustomViews.StaticNumericValueNodeCustomView"
|
||||||
|
x:DataType="customViewModels:StaticNumericValueNodeCustomViewModel">
|
||||||
|
<UserControl.Resources>
|
||||||
|
<converters:NumericConverter x:Key="NumericConverter" />
|
||||||
|
</UserControl.Resources>
|
||||||
|
<NumericUpDown VerticalAlignment="Center" Value="{Binding Node.Storage, Converter={StaticResource NumericConverter}}"></NumericUpDown>
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
|
||||||
|
namespace Artemis.VisualScripting.Nodes.CustomViews
|
||||||
|
{
|
||||||
|
public partial class StaticNumericValueNodeCustomView : UserControl
|
||||||
|
{
|
||||||
|
public StaticNumericValueNodeCustomView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,15 +0,0 @@
|
|||||||
<UserControl x:Class="Artemis.VisualScripting.Nodes.CustomViews.StaticNumericValueNodeCustomView"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
d:DesignHeight="450" d:DesignWidth="800">
|
|
||||||
<UserControl.Resources>
|
|
||||||
<shared:StringToNumericConverter x:Key="StringToNumericConverter" />
|
|
||||||
</UserControl.Resources>
|
|
||||||
<TextBox VerticalAlignment="Center"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
Text="{Binding Node.Storage, Converter={StaticResource StringToNumericConverter}}" />
|
|
||||||
</UserControl>
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
<UserControl x:Class="Artemis.VisualScripting.Nodes.CustomViews.StaticStringValueNodeCustomView"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
d:DesignHeight="450" d:DesignWidth="800">
|
|
||||||
<TextBox VerticalAlignment="Center"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
Text="{Binding Node.Storage}" />
|
|
||||||
</UserControl>
|
|
||||||
Loading…
x
Reference in New Issue
Block a user