diff --git a/src/Artemis.Core/Utilities/Numeric.cs b/src/Artemis.Core/Utilities/Numeric.cs index ac779d557..d96516148 100644 --- a/src/Artemis.Core/Utilities/Numeric.cs +++ b/src/Artemis.Core/Utilities/Numeric.cs @@ -257,7 +257,7 @@ namespace Artemis.Core /// /// Returns a boolean indicating whether the provided type can be used as a . /// - public static bool IsTypeCompatible(Type type) + public static bool IsTypeCompatible(Type? type) { return type == typeof(Numeric) || type == typeof(float) || diff --git a/src/Artemis.Core/VisualScripting/InputPin.cs b/src/Artemis.Core/VisualScripting/InputPin.cs index 2d2320962..2b218fb44 100644 --- a/src/Artemis.Core/VisualScripting/InputPin.cs +++ b/src/Artemis.Core/VisualScripting/InputPin.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using Newtonsoft.Json; namespace Artemis.Core @@ -76,7 +78,7 @@ namespace Artemis.Core internal InputPin(INode node, Type type, string name) : base(node, name) { - Type = type; + _type = type; _value = type.GetDefault(); } @@ -84,6 +86,25 @@ namespace Artemis.Core #region Methods + /// + /// Changes the type of this pin, disconnecting any pins that are incompatible with the new type. + /// + /// The new type of the pin. + public void ChangeType(Type type) + { + if (_type == type) + return; + + // Disconnect pins incompatible with the new type + List toDisconnect = ConnectedTo.Where(p => !p.IsTypeCompatible(type)).ToList(); + foreach (IPin pin in toDisconnect) + DisconnectFrom(pin); + + // Change the type + SetAndNotify(ref _type, type, nameof(Type)); + Value = type.GetDefault(); + } + private void Evaluate() { if (Type.IsValueType) @@ -104,7 +125,7 @@ namespace Artemis.Core #region Properties & Fields /// - public override Type Type { get; } + public override Type Type => _type; /// public override object? PinValue => Value; @@ -113,6 +134,7 @@ namespace Artemis.Core public override PinDirection Direction => PinDirection.Input; private object? _value; + private Type _type; /// /// Gets or sets the value of the input pin diff --git a/src/Artemis.Core/VisualScripting/InputPinCollection.cs b/src/Artemis.Core/VisualScripting/InputPinCollection.cs index fd4da2bf0..9c2afde00 100644 --- a/src/Artemis.Core/VisualScripting/InputPinCollection.cs +++ b/src/Artemis.Core/VisualScripting/InputPinCollection.cs @@ -59,12 +59,14 @@ namespace Artemis.Core /// public sealed class InputPinCollection : PinCollection { + private Type _type; + #region Constructors internal InputPinCollection(INode node, Type type, string name, int initialCount) : base(node, name) { - Type = type; + _type = type; // Can't do this in the base constructor because the type won't be set yet for (int i = 0; i < initialCount; i++) @@ -75,6 +77,20 @@ namespace Artemis.Core #region Methods + /// + /// Changes the type of this pin, disconnecting any pins that are incompatible with the new type. + /// + /// The new type of the pin. + public void ChangeType(Type type) + { + if (_type == type) + return; + + foreach (IPin pin in Pins) + (pin as InputPin)?.ChangeType(type); + SetAndNotify(ref _type, type, nameof(Type)); + } + /// public override IPin CreatePin() { @@ -89,17 +105,12 @@ namespace Artemis.Core public override PinDirection Direction => PinDirection.Input; /// - public override Type Type { get; } - - /// - /// Gets an enumerable of the pins in this collection - /// - public new IEnumerable Pins => base.Pins.Cast(); + public override Type Type => _type; /// /// Gets an enumerable of the values of the pins in this collection /// - public IEnumerable Values => Pins.Where(p => p.Value != null).Select(p => p.Value); + public IEnumerable Values => Pins.Where(p => p.PinValue != null).Select(p => p.PinValue); #endregion } diff --git a/src/Artemis.Core/VisualScripting/Interfaces/IPin.cs b/src/Artemis.Core/VisualScripting/Interfaces/IPin.cs index 22afe5f48..197f40365 100644 --- a/src/Artemis.Core/VisualScripting/Interfaces/IPin.cs +++ b/src/Artemis.Core/VisualScripting/Interfaces/IPin.cs @@ -75,5 +75,12 @@ namespace Artemis.Core /// Disconnects all pins this pin is connected to /// void DisconnectAll(); + + /// + /// Determines whether this pin is compatible with the given type + /// + /// The type to check for compatibility + /// if the type is compatible, otherwise . + public bool IsTypeCompatible(Type type); } } \ No newline at end of file diff --git a/src/Artemis.Core/VisualScripting/OutputPin.cs b/src/Artemis.Core/VisualScripting/OutputPin.cs index e7dc10bd5..a2b1ce1f0 100644 --- a/src/Artemis.Core/VisualScripting/OutputPin.cs +++ b/src/Artemis.Core/VisualScripting/OutputPin.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using Newtonsoft.Json; namespace Artemis.Core @@ -66,16 +68,36 @@ namespace Artemis.Core internal OutputPin(INode node, Type type, string name) : base(node, name) { - Type = type; + _type = type; _value = type.GetDefault(); } #endregion + #region Methods + + /// + /// Changes the type of this pin, disconnecting any pins that are incompatible with the new type. + /// + /// The new type of the pin. + public void ChangeType(Type type) + { + // Disconnect pins incompatible with the new type + List toDisconnect = ConnectedTo.Where(p => !p.IsTypeCompatible(type)).ToList(); + foreach (IPin pin in toDisconnect) + DisconnectFrom(pin); + + // Change the type + SetAndNotify(ref _type, type, nameof(Type)); + Value = type.GetDefault(); + } + + #endregion + #region Properties & Fields /// - public override Type Type { get; } + public override Type Type => _type; /// public override object? PinValue => Value; @@ -84,6 +106,7 @@ namespace Artemis.Core public override PinDirection Direction => PinDirection.Output; private object? _value; + private Type _type; /// /// Gets or sets the value of the output pin diff --git a/src/Artemis.Core/VisualScripting/Pin.cs b/src/Artemis.Core/VisualScripting/Pin.cs index 0d48693af..46916fae5 100644 --- a/src/Artemis.Core/VisualScripting/Pin.cs +++ b/src/Artemis.Core/VisualScripting/Pin.cs @@ -101,6 +101,9 @@ namespace Artemis.Core /// public void DisconnectAll() { + if (!_connectedTo.Any()) + return; + List connectedPins = new(_connectedTo); _connectedTo.Clear(); @@ -114,6 +117,12 @@ namespace Artemis.Core OnPropertyChanged(nameof(ConnectedTo)); } + /// + public bool IsTypeCompatible(Type type) => Type == type + || Type == typeof(Enum) && type.IsEnum + || Direction == PinDirection.Input && Type == typeof(object) + || Direction == PinDirection.Output && type == typeof(object); + #endregion } } \ No newline at end of file diff --git a/src/Artemis.Core/VisualScripting/PinCollection.cs b/src/Artemis.Core/VisualScripting/PinCollection.cs index 5c060d884..fd04d57f8 100644 --- a/src/Artemis.Core/VisualScripting/PinCollection.cs +++ b/src/Artemis.Core/VisualScripting/PinCollection.cs @@ -7,7 +7,7 @@ using Artemis.Core.Events; namespace Artemis.Core { /// - public abstract class PinCollection : IPinCollection + public abstract class PinCollection : CorePropertyChanged, IPinCollection { #region Constructors diff --git a/src/Avalonia/Artemis.UI.Shared/Controls/DataModelPicker/DataModelPicker.cs b/src/Avalonia/Artemis.UI.Shared/Controls/DataModelPicker/DataModelPicker.cs index d8efea8de..0a9f50f3e 100644 --- a/src/Avalonia/Artemis.UI.Shared/Controls/DataModelPicker/DataModelPicker.cs +++ b/src/Avalonia/Artemis.UI.Shared/Controls/DataModelPicker/DataModelPicker.cs @@ -7,6 +7,7 @@ using Artemis.Core; using Artemis.Core.Modules; using Artemis.UI.Shared.DataModelVisualization.Shared; using Artemis.UI.Shared.Events; +using Artemis.UI.Shared.Extensions; using Artemis.UI.Shared.Services.Interfaces; using Avalonia; using Avalonia.Controls; @@ -267,8 +268,10 @@ public class DataModelPicker : TemplatedControl if (selected is DataModelPropertyViewModel property && property.DataModelPath != null) DataModelPath = new DataModelPath(property.DataModelPath); - if (selected is DataModelListViewModel list && list.DataModelPath != null) + else if (selected is DataModelListViewModel list && list.DataModelPath != null) DataModelPath = new DataModelPath(list.DataModelPath); + else if (selected is DataModelEventViewModel dataModelEvent && dataModelEvent.DataModelPath != null) + DataModelPath = new DataModelPath(dataModelEvent.DataModelPath); } private void UpdateCurrentPath(bool selectCurrentPath) @@ -292,24 +295,8 @@ public class DataModelPicker : TemplatedControl _currentPathDisplay.Text = string.Join(" › ", DataModelPath.Segments.Where(s => s.GetPropertyDescription() != null).Select(s => s.GetPropertyDescription()!.Name)); if (_currentPathDescription != null) _currentPathDescription.Text = DataModelPath.GetPropertyDescription()?.Description; - if (_currentPathIcon != null) - { - Type? type = DataModelPath.GetPropertyType(); - if (type == null) - _currentPathIcon.Kind = MaterialIconKind.QuestionMarkCircle; - else if (type.TypeIsNumber()) - _currentPathIcon.Kind = MaterialIconKind.CalculatorVariantOutline; - else if (type.IsEnum) - _currentPathIcon.Kind = MaterialIconKind.FormatListBulletedSquare; - else if (type == typeof(bool)) - _currentPathIcon.Kind = MaterialIconKind.CircleHalfFull; - else if (type == typeof(string)) - _currentPathIcon.Kind = MaterialIconKind.Text; - else if (type == typeof(SKColor)) - _currentPathIcon.Kind = MaterialIconKind.Palette; - else - _currentPathIcon.Kind = MaterialIconKind.Matrix; - } + _currentPathIcon.Kind = DataModelPath.GetPropertyType().GetTypeIcon(); + } } \ No newline at end of file diff --git a/src/Avalonia/Artemis.UI.Shared/Controls/DataModelPicker/DataModelPickerButton.cs b/src/Avalonia/Artemis.UI.Shared/Controls/DataModelPicker/DataModelPickerButton.cs index 3fd1786e9..2955c92f4 100644 --- a/src/Avalonia/Artemis.UI.Shared/Controls/DataModelPicker/DataModelPickerButton.cs +++ b/src/Avalonia/Artemis.UI.Shared/Controls/DataModelPicker/DataModelPickerButton.cs @@ -32,6 +32,12 @@ public class DataModelPickerButton : TemplatedControl public static readonly StyledProperty ShowFullPathProperty = AvaloniaProperty.Register(nameof(ShowFullPath)); + /// + /// Gets or sets a boolean indicating whether the button should show the icon of the first provided filter type. + /// + public static readonly StyledProperty ShowTypeIconProperty = + AvaloniaProperty.Register(nameof(ShowTypeIcon)); + /// /// Gets a boolean indicating whether the data model picker has a value. /// @@ -77,6 +83,7 @@ public class DataModelPickerButton : TemplatedControl private bool _attached; private bool _flyoutActive; private Button? _button; + private TextBlock? _label; private DataModelPickerFlyout? _flyout; static DataModelPickerButton() @@ -103,6 +110,16 @@ public class DataModelPickerButton : TemplatedControl set => SetValue(ShowFullPathProperty, value); } + + /// + /// Gets or sets a boolean indicating whether the button should show the icon of the first provided filter type. + /// + public bool ShowTypeIcon + { + get => GetValue(ShowTypeIconProperty); + set => SetValue(ShowTypeIconProperty, value); + } + /// /// Gets a boolean indicating whether the data model picker has a value. /// @@ -212,13 +229,13 @@ public class DataModelPickerButton : TemplatedControl { HasValue = DataModelPath != null && DataModelPath.IsValid; - if (_button == null) + if (_button == null || _label == null) return; if (!HasValue) { ToolTip.SetTip(_button, null); - _button.Content = Placeholder; + _label.Text = Placeholder; } else { @@ -227,7 +244,7 @@ public class DataModelPickerButton : TemplatedControl formattedPath = string.Join(" › ", DataModelPath.Segments.Where(s => s.GetPropertyDescription() != null).Select(s => s.GetPropertyDescription()!.Name)); ToolTip.SetTip(_button, formattedPath); - _button.Content = ShowFullPath + _label.Text = ShowFullPath ? formattedPath : DataModelPath?.Segments.LastOrDefault()?.GetPropertyDescription()?.Name ?? DataModelPath?.Segments.LastOrDefault()?.Identifier; } @@ -261,6 +278,7 @@ public class DataModelPickerButton : TemplatedControl _button.Click -= OnButtonClick; base.OnApplyTemplate(e); _button = e.NameScope.Find