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

Node editor - Added VM structure

Debugger - Run updates on UI thread
This commit is contained in:
Robert 2022-03-11 17:22:29 +01:00
parent 4c274a1749
commit c99224ab2d
32 changed files with 825 additions and 232 deletions

View File

@ -6,7 +6,7 @@ namespace Artemis.UI.Shared
/// <summary>
/// Represents a view model for a plugin configuration window
/// </summary>
public abstract class PluginConfigurationViewModel : ViewModelBase, IPluginConfigurationViewModel
public abstract class PluginConfigurationViewModel : ViewModelValidationBase, IPluginConfigurationViewModel
{
/// <summary>
/// Creates a new instance of the <see cref="PluginConfigurationViewModel" /> class

View File

@ -60,6 +60,33 @@ public abstract class ViewModelValidationBase : ReactiveValidationObject
get => _displayName;
set => this.RaiseAndSetIfChanged(ref _displayName, value);
}
/// <summary>
/// RaiseAndSetIfChanged fully implements a Setter for a read-write property on a ReactiveObject, using
/// CallerMemberName to raise the notification and the ref to the backing field to set the property.
/// </summary>
/// <typeparam name="TRet">The type of the return value.</typeparam>
/// <param name="backingField">A Reference to the backing field for this property.</param>
/// <param name="newValue">The new value.</param>
/// <param name="propertyName">
/// The name of the property, usually automatically provided through the CallerMemberName
/// attribute.
/// </param>
/// <returns>The newly set value, normally discarded.</returns>
[NotifyPropertyChangedInvocator]
public TRet RaiseAndSetIfChanged<TRet>(ref TRet backingField, TRet newValue, [CallerMemberName] string? propertyName = null)
{
if (propertyName is null)
throw new ArgumentNullException(nameof(propertyName));
if (EqualityComparer<TRet>.Default.Equals(backingField, newValue))
return newValue;
this.RaisePropertyChanging(propertyName);
backingField = newValue;
this.RaisePropertyChanged(propertyName);
return newValue;
}
}
/// <summary>

View File

@ -90,6 +90,11 @@
"Splat": "14.1.45"
}
},
"ArtemisRGB.Plugins.BuildTask": {
"type": "Transitive",
"resolved": "1.1.0",
"contentHash": "B8A5NkUEzTUgc5M/QACfyjIF5M23EUzSx8A8l/owJtB0bgmij6y/MQW1i/PcS3EDFQRothBOUuC3BCPY5UoRRQ=="
},
"Avalonia.Angle.Windows.Natives": {
"type": "Transitive",
"resolved": "2.1.0.2020091801",
@ -560,6 +565,11 @@
"Ninject": "3.3.3"
}
},
"OpenRGB.NET": {
"type": "Transitive",
"resolved": "1.7.0",
"contentHash": "12pMEUaeoG8mN707QRO9hdT529+UnqUpwMW1H/gDTMsJrerhJve6Yt5Dnheu1isQB4PWP1wu3IDVbHCchznkiw=="
},
"ReactiveUI.Validation": {
"type": "Transitive",
"resolved": "2.2.1",
@ -1762,6 +1772,21 @@
"System.ValueTuple": "4.5.0"
}
},
"artemis.plugins.devices.openrgb": {
"type": "Project",
"dependencies": {
"ArtemisRGB.Plugins.BuildTask": "1.1.0",
"Avalonia": "0.10.11",
"Avalonia.Controls.DataGrid": "0.10.11",
"Avalonia.ReactiveUI": "0.10.11",
"Material.Icons.Avalonia": "1.0.2",
"OpenRGB.NET": "1.7.0",
"RGB.NET.Core": "1.0.0-prerelease7",
"ReactiveUI": "16.3.10",
"Serilog": "2.10.0",
"SkiaSharp": "2.88.0-preview.178"
}
},
"artemis.storage": {
"type": "Project",
"dependencies": {

View File

@ -14,6 +14,8 @@ using Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers;
using Artemis.UI.Screens.Settings;
using Artemis.UI.Screens.Sidebar;
using Artemis.UI.Screens.SurfaceEditor;
using Artemis.UI.Screens.VisualScripting;
using Artemis.UI.Screens.VisualScripting.Pins;
using Artemis.UI.Services;
using DynamicData.Binding;
using ReactiveUI;
@ -87,4 +89,15 @@ namespace Artemis.UI.Ninject.Factories
ITreePropertyViewModel TreePropertyViewModel(ILayerProperty layerProperty, PropertyViewModel propertyViewModel);
ITimelinePropertyViewModel TimelinePropertyViewModel(ILayerProperty layerProperty, PropertyViewModel propertyViewModel);
}
public interface INodeVmFactory
{
NodeScriptViewModel NodeScriptViewModel(NodeScript nodeScript);
NodePickerViewModel NodePickerViewModel(NodeScript nodeScript);
NodeViewModel NodeViewModel(INode node);
InputPinCollectionViewModel<T> InputPinCollectionViewModel<T>(InputPinCollection<T> inputPinCollection);
InputPinViewModel<T> InputPinViewModel<T>(InputPin<T> inputPin);
OutputPinCollectionViewModel<T> OutputPinCollectionViewModel<T>(OutputPinCollection<T> outputPinCollection);
OutputPinViewModel<T> OutputPinViewModel<T>(OutputPin<T> outputPin);
}
}

View File

@ -4,23 +4,23 @@ using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Timers;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.Core.Services;
using Artemis.UI.Shared;
using Artemis.UI.Shared.DataModelVisualization.Shared;
using Artemis.UI.Shared.Services.Interfaces;
using Avalonia.Threading;
using DynamicData;
using ReactiveUI;
namespace Artemis.UI.Screens.Debugger.DataModel
{
namespace Artemis.UI.Screens.Debugger.DataModel;
public class DataModelDebugViewModel : ActivatableViewModelBase, IRoutableViewModel
{
private readonly IDataModelUIService _dataModelUIService;
private readonly IPluginManagementService _pluginManagementService;
private readonly Timer _updateTimer;
private readonly DispatcherTimer _updateTimer;
private bool _isModuleFilterEnabled;
private DataModelPropertiesViewModel? _mainDataModel;
@ -32,8 +32,7 @@ namespace Artemis.UI.Screens.Debugger.DataModel
{
_dataModelUIService = dataModelUIService;
_pluginManagementService = pluginManagementService;
_updateTimer = new Timer(25);
_updateTimer.Elapsed += UpdateTimerOnElapsed;
_updateTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(25), DispatcherPriority.Normal, (_, _) => Update());
HostScreen = hostScreen;
Modules = new ObservableCollection<Module>();
@ -53,9 +52,6 @@ namespace Artemis.UI.Screens.Debugger.DataModel
});
}
public string UrlPathSegment => "data-model";
public IScreen HostScreen { get; }
public DataModelPropertiesViewModel? MainDataModel
{
get => _mainDataModel;
@ -74,7 +70,7 @@ namespace Artemis.UI.Screens.Debugger.DataModel
set
{
RaiseAndSetIfChanged(ref _slowUpdates, value);
_updateTimer.Interval = _slowUpdates ? 500 : 25;
_updateTimer.Interval = TimeSpan.FromMilliseconds(_slowUpdates ? 500 : 25);
}
}
@ -103,7 +99,8 @@ namespace Artemis.UI.Screens.Debugger.DataModel
GetDataModel();
}
}
private void UpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
private void Update()
{
if (MainDataModel == null)
return;
@ -123,7 +120,7 @@ namespace Artemis.UI.Screens.Debugger.DataModel
private void GetDataModel()
{
MainDataModel = SelectedModule != null
? _dataModelUIService.GetPluginDataModelVisualization(new List<Module>() { SelectedModule }, false)
? _dataModelUIService.GetPluginDataModelVisualization(new List<Module> {SelectedModule}, false)
: _dataModelUIService.GetMainDataModelVisualization();
}
@ -141,7 +138,11 @@ namespace Artemis.UI.Screens.Debugger.DataModel
_dataModelUIService.UpdateModules(MainDataModel);
}
else if (!SelectedModule.IsEnabled)
{
SelectedModule = null;
}
}
public string UrlPathSegment => "data-model";
public IScreen HostScreen { get; }
}

View File

@ -3,19 +3,20 @@ using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Timers;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Shared;
using Avalonia.Threading;
using ReactiveUI;
using SkiaSharp;
namespace Artemis.UI.Screens.Debugger.Performance
{
namespace Artemis.UI.Screens.Debugger.Performance;
public class PerformanceDebugViewModel : ActivatableViewModelBase, IRoutableViewModel
{
private readonly ICoreService _coreService;
private readonly IPluginManagementService _pluginManagementService;
private readonly DispatcherTimer _updateTimer;
private double _currentFps;
private string? _renderer;
private int _renderHeight;
@ -26,9 +27,7 @@ namespace Artemis.UI.Screens.Debugger.Performance
HostScreen = hostScreen;
_coreService = coreService;
_pluginManagementService = pluginManagementService;
Timer updateTimer = new(500);
updateTimer.Elapsed += UpdateTimerOnElapsed;
_updateTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(500), DispatcherPriority.Normal, (_, _) => Update());
this.WhenActivated(disposables =>
{
@ -47,20 +46,17 @@ namespace Artemis.UI.Screens.Debugger.Performance
HandleActivation();
PopulateItems();
updateTimer.Start();
_updateTimer.Start();
Disposable.Create(() =>
{
updateTimer.Stop();
_updateTimer.Stop();
Items.Clear();
HandleDeactivation();
}).DisposeWith(disposables);
});
}
public string UrlPathSegment => "performance";
public IScreen HostScreen { get; }
public ObservableCollection<PerformanceDebugPluginViewModel> Items { get; } = new();
public double CurrentFps
@ -113,7 +109,7 @@ namespace Artemis.UI.Screens.Debugger.Performance
PopulateItems();
}
private void UpdateTimerOnElapsed(object? sender, ElapsedEventArgs e)
private void Update()
{
foreach (PerformanceDebugPluginViewModel viewModel in Items)
viewModel.Update();
@ -127,5 +123,8 @@ namespace Artemis.UI.Screens.Debugger.Performance
RenderHeight = bitmapInfo.Height;
RenderWidth = bitmapInfo.Width;
}
}
public string UrlPathSegment => "performance";
public IScreen HostScreen { get; }
}

View File

@ -0,0 +1,8 @@
<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"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.VisualScripting.CableView">
Welcome to Avalonia!
</UserControl>

View File

@ -0,0 +1,20 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.VisualScripting
{
public partial class CableView : ReactiveUserControl<CableViewModel>
{
public CableView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

View File

@ -0,0 +1,28 @@
using Artemis.UI.Screens.VisualScripting.Pins;
using Artemis.UI.Shared;
namespace Artemis.UI.Screens.VisualScripting;
public class CableViewModel : ActivatableViewModelBase
{
private PinViewModel _from;
private PinViewModel _to;
public CableViewModel(PinViewModel from, PinViewModel to)
{
_from = from;
_to = to;
}
public PinViewModel From
{
get => _from;
set => RaiseAndSetIfChanged(ref _from, value);
}
public PinViewModel To
{
get => _to;
set => RaiseAndSetIfChanged(ref _to, value);
}
}

View File

@ -0,0 +1,45 @@
<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:visualScripting="clr-namespace:Artemis.UI.Screens.VisualScripting"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.VisualScripting.NodePickerView"
x:DataType="visualScripting:NodePickerViewModel">
<UserControl.Styles>
<Style Selector="Border.picker-container">
</Style>
<Style Selector="Border.picker-container-hidden">
<Style.Animations>
<Animation Duration="0:0:1">
<KeyFrame Cue="0%">
<Setter Property="Opacity" Value="1.0" />
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="Opacity" Value="0.0" />
<Setter Property="IsVisible" Value="False" />
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
<Style Selector="Border.picker-container-visible">
<Style.Animations>
<Animation Duration="0:0:1">
<KeyFrame Cue="0%">
<Setter Property="Opacity" Value="0.0" />
<Setter Property="IsVisible" Value="True" />
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="Opacity" Value="1.0" />
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
</UserControl.Styles>
<Border Classes="grid picker-container"
Classes.picker-container-hidden="{CompiledBinding !IsVisible}"
Classes.picker-container-visible="{CompiledBinding !IsVisible}">
<TextBlock>Test</TextBlock>
</Border>
</UserControl>

View File

@ -0,0 +1,20 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.VisualScripting
{
public partial class NodePickerView : ReactiveUserControl<NodePickerViewModel>
{
public NodePickerView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

View File

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Artemis.Core.Services;
using Artemis.UI.Shared;
using Avalonia;
namespace Artemis.UI.Screens.VisualScripting;
public class NodePickerViewModel : ActivatableViewModelBase
{
private readonly INodeService _nodeService;
private bool _isVisible;
private Point _position;
public bool IsVisible
{
get => _isVisible;
set => RaiseAndSetIfChanged(ref _isVisible, value);
}
public Point Position
{
get => _position;
set => RaiseAndSetIfChanged(ref _position, value);
}
public NodePickerViewModel(INodeService nodeService)
{
_nodeService = nodeService;
}
public void Show(Point position)
{
IsVisible = true;
Position = position;
}
public void Hide()
{
IsVisible = false;
}
}

View File

@ -0,0 +1,19 @@
<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:visualScripting="clr-namespace:Artemis.UI.Screens.VisualScripting"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.VisualScripting.NodeScriptView"
x:DataType="visualScripting:NodeScriptViewModel">
<Canvas PointerReleased="InputElement_OnPointerReleased">
<!-- Cables -->
<!-- Nodes -->
<!-- Flyout -->
<ContentControl Content="{CompiledBinding NodePickerViewModel}"
Canvas.Left="{CompiledBinding NodePickerViewModel.Position.X}"
Canvas.Top="{CompiledBinding NodePickerViewModel.Position.Y}"/>
</Canvas>
</UserControl>

View File

@ -0,0 +1,29 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.VisualScripting
{
public partial class NodeScriptView : ReactiveUserControl<NodeScriptViewModel>
{
public NodeScriptView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void InputElement_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
{
if (e.InitialPressMouseButton == MouseButton.Right)
ViewModel?.ShowNodePicker(e.GetCurrentPoint(this).Position);
if (e.InitialPressMouseButton == MouseButton.Left)
ViewModel?.HideNodePicker();
}
}
}

View File

@ -0,0 +1,70 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Linq;
using Artemis.Core;
using Artemis.Core.Events;
using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Shared;
using Avalonia;
using Avalonia.Controls.Mixins;
using ReactiveUI;
namespace Artemis.UI.Screens.VisualScripting;
public class NodeScriptViewModel : ActivatableViewModelBase
{
private readonly INodeService _nodeService;
private readonly INodeVmFactory _nodeVmFactory;
public NodeScriptViewModel(NodeScript nodeScript, INodeVmFactory nodeVmFactory, INodeService nodeService)
{
_nodeVmFactory = nodeVmFactory;
_nodeService = nodeService;
NodeScript = nodeScript;
NodePickerViewModel = _nodeVmFactory.NodePickerViewModel(nodeScript);
this.WhenActivated(d =>
{
Observable.FromEventPattern<SingleValueEventArgs<INode>>(x => NodeScript.NodeAdded += x, x => NodeScript.NodeAdded -= x)
.Subscribe(e => HandleNodeAdded(e.EventArgs))
.DisposeWith(d);
Observable.FromEventPattern<SingleValueEventArgs<INode>>(x => NodeScript.NodeRemoved += x, x => NodeScript.NodeRemoved -= x)
.Subscribe(e => HandleNodeRemoved(e.EventArgs))
.DisposeWith(d);
});
NodeViewModels = new ObservableCollection<NodeViewModel>();
foreach (INode nodeScriptNode in NodeScript.Nodes)
NodeViewModels.Add(_nodeVmFactory.NodeViewModel(nodeScriptNode));
}
public NodeScript NodeScript { get; }
public ObservableCollection<NodeViewModel> NodeViewModels { get; }
public NodePickerViewModel NodePickerViewModel { get; }
private void HandleNodeAdded(SingleValueEventArgs<INode> eventArgs)
{
NodeViewModels.Add(_nodeVmFactory.NodeViewModel(eventArgs.Value));
}
private void HandleNodeRemoved(SingleValueEventArgs<INode> eventArgs)
{
NodeViewModel? toRemove = NodeViewModels.FirstOrDefault(vm => vm.Node == eventArgs.Value);
if (toRemove != null)
NodeViewModels.Remove(toRemove);
}
public void ShowNodePicker(Point position)
{
NodePickerViewModel.Show(position);
}
public void HideNodePicker()
{
NodePickerViewModel.Hide();
}
}

View File

@ -0,0 +1,8 @@
<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"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.VisualScripting.NodeView">
Welcome to Avalonia!
</UserControl>

View File

@ -0,0 +1,20 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.VisualScripting
{
public partial class NodeView : ReactiveUserControl<NodeViewModel>
{
public NodeView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

View File

@ -0,0 +1,23 @@
using Artemis.Core;
using Artemis.UI.Shared;
using Avalonia;
namespace Artemis.UI.Screens.VisualScripting;
public class NodeViewModel : ActivatableViewModelBase
{
private Point _position;
public NodeViewModel(INode node)
{
Node = node;
}
public INode Node { get; }
public Point Position
{
get => _position;
set => RaiseAndSetIfChanged(ref _position, value);
}
}

View File

@ -0,0 +1,8 @@
<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"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.VisualScripting.Pins.InputPinCollectionView">
Welcome to Avalonia!
</UserControl>

View File

@ -0,0 +1,20 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.VisualScripting.Pins
{
public partial class InputPinCollectionView : ReactiveUserControl<PinCollectionViewModel>
{
public InputPinCollectionView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

View File

@ -0,0 +1,13 @@
using Artemis.Core;
namespace Artemis.UI.Screens.VisualScripting.Pins;
public class InputPinCollectionViewModel<T> : PinCollectionViewModel
{
public InputPinCollection<T> InputPinCollection { get; }
public InputPinCollectionViewModel(InputPinCollection<T> inputPinCollection) : base(inputPinCollection)
{
InputPinCollection = inputPinCollection;
}
}

View File

@ -0,0 +1,8 @@
<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"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.VisualScripting.Pins.InputPinView">
Welcome to Avalonia!
</UserControl>

View File

@ -0,0 +1,20 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.VisualScripting.Pins
{
public partial class InputPinView : ReactiveUserControl<PinViewModel>
{
public InputPinView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

View File

@ -0,0 +1,13 @@
using Artemis.Core;
namespace Artemis.UI.Screens.VisualScripting.Pins;
public class InputPinViewModel<T> : PinViewModel
{
public InputPin<T> InputPin { get; }
public InputPinViewModel(InputPin<T> inputPin) : base(inputPin)
{
InputPin = inputPin;
}
}

View File

@ -0,0 +1,8 @@
<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"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.VisualScripting.Pins.OutputPinCollectionView">
Welcome to Avalonia!
</UserControl>

View File

@ -0,0 +1,20 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.VisualScripting.Pins
{
public partial class OutputPinCollectionView : ReactiveUserControl<PinCollectionViewModel>
{
public OutputPinCollectionView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

View File

@ -0,0 +1,13 @@
using Artemis.Core;
namespace Artemis.UI.Screens.VisualScripting.Pins;
public class OutputPinCollectionViewModel<T> : PinCollectionViewModel
{
public OutputPinCollection<T> OutputPinCollection { get; }
public OutputPinCollectionViewModel(OutputPinCollection<T> outputPinCollection) : base(outputPinCollection)
{
OutputPinCollection = outputPinCollection;
}
}

View File

@ -0,0 +1,8 @@
<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"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.VisualScripting.Pins.OutputPinView">
Welcome to Avalonia!
</UserControl>

View File

@ -0,0 +1,20 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.VisualScripting.Pins
{
public partial class OutputPinView : ReactiveUserControl<PinViewModel>
{
public OutputPinView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

View File

@ -0,0 +1,13 @@
using Artemis.Core;
namespace Artemis.UI.Screens.VisualScripting.Pins;
public class OutputPinViewModel<T> : PinViewModel
{
public OutputPin<T> OutputPin { get; }
public OutputPinViewModel(OutputPin<T> outputPin) : base(outputPin)
{
OutputPin = outputPin;
}
}

View File

@ -0,0 +1,14 @@
using Artemis.Core;
using Artemis.UI.Shared;
namespace Artemis.UI.Screens.VisualScripting.Pins;
public abstract class PinCollectionViewModel : ActivatableViewModelBase
{
public PinCollection PinCollection { get; }
protected PinCollectionViewModel(PinCollection pinCollection)
{
PinCollection = pinCollection;
}
}

View File

@ -0,0 +1,14 @@
using Artemis.Core;
using Artemis.UI.Shared;
namespace Artemis.UI.Screens.VisualScripting.Pins;
public abstract class PinViewModel : ActivatableViewModelBase
{
protected PinViewModel(IPin pin)
{
Pin = pin;
}
public IPin Pin { get; }
}