diff --git a/src/Avalonia/Artemis.UI.Shared/Plugins/PluginConfigurationViewModel.cs b/src/Avalonia/Artemis.UI.Shared/Plugins/PluginConfigurationViewModel.cs
index dbed50afa..4fcfaa4fd 100644
--- a/src/Avalonia/Artemis.UI.Shared/Plugins/PluginConfigurationViewModel.cs
+++ b/src/Avalonia/Artemis.UI.Shared/Plugins/PluginConfigurationViewModel.cs
@@ -6,7 +6,7 @@ namespace Artemis.UI.Shared
///
/// Represents a view model for a plugin configuration window
///
- public abstract class PluginConfigurationViewModel : ViewModelBase, IPluginConfigurationViewModel
+ public abstract class PluginConfigurationViewModel : ViewModelValidationBase, IPluginConfigurationViewModel
{
///
/// Creates a new instance of the class
diff --git a/src/Avalonia/Artemis.UI.Shared/ViewModelBase.cs b/src/Avalonia/Artemis.UI.Shared/ViewModelBase.cs
index e89b30258..d28200871 100644
--- a/src/Avalonia/Artemis.UI.Shared/ViewModelBase.cs
+++ b/src/Avalonia/Artemis.UI.Shared/ViewModelBase.cs
@@ -60,6 +60,33 @@ public abstract class ViewModelValidationBase : ReactiveValidationObject
get => _displayName;
set => this.RaiseAndSetIfChanged(ref _displayName, value);
}
+
+ ///
+ /// 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.
+ ///
+ /// The type of the return value.
+ /// A Reference to the backing field for this property.
+ /// The new value.
+ ///
+ /// The name of the property, usually automatically provided through the CallerMemberName
+ /// attribute.
+ ///
+ /// The newly set value, normally discarded.
+ [NotifyPropertyChangedInvocator]
+ public TRet RaiseAndSetIfChanged(ref TRet backingField, TRet newValue, [CallerMemberName] string? propertyName = null)
+ {
+ if (propertyName is null)
+ throw new ArgumentNullException(nameof(propertyName));
+
+ if (EqualityComparer.Default.Equals(backingField, newValue))
+ return newValue;
+
+ this.RaisePropertyChanging(propertyName);
+ backingField = newValue;
+ this.RaisePropertyChanged(propertyName);
+ return newValue;
+ }
}
///
diff --git a/src/Avalonia/Artemis.UI.Windows/packages.lock.json b/src/Avalonia/Artemis.UI.Windows/packages.lock.json
index b7a683c5b..8ce041f51 100644
--- a/src/Avalonia/Artemis.UI.Windows/packages.lock.json
+++ b/src/Avalonia/Artemis.UI.Windows/packages.lock.json
@@ -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": {
diff --git a/src/Avalonia/Artemis.UI/Ninject/Factories/IVMFactory.cs b/src/Avalonia/Artemis.UI/Ninject/Factories/IVMFactory.cs
index b7163bcd3..d575bfbc3 100644
--- a/src/Avalonia/Artemis.UI/Ninject/Factories/IVMFactory.cs
+++ b/src/Avalonia/Artemis.UI/Ninject/Factories/IVMFactory.cs
@@ -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 InputPinCollectionViewModel(InputPinCollection inputPinCollection);
+ InputPinViewModel InputPinViewModel(InputPin inputPin);
+ OutputPinCollectionViewModel OutputPinCollectionViewModel(OutputPinCollection outputPinCollection);
+ OutputPinViewModel OutputPinViewModel(OutputPin outputPin);
+ }
}
\ No newline at end of file
diff --git a/src/Avalonia/Artemis.UI/Screens/Debugger/Tabs/DataModel/DataModelDebugViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Debugger/Tabs/DataModel/DataModelDebugViewModel.cs
index 9c62836b8..874e5c75e 100644
--- a/src/Avalonia/Artemis.UI/Screens/Debugger/Tabs/DataModel/DataModelDebugViewModel.cs
+++ b/src/Avalonia/Artemis.UI/Screens/Debugger/Tabs/DataModel/DataModelDebugViewModel.cs
@@ -4,144 +4,145 @@ 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
{
- public class DataModelDebugViewModel : ActivatableViewModelBase, IRoutableViewModel
+ private readonly IDataModelUIService _dataModelUIService;
+ private readonly IPluginManagementService _pluginManagementService;
+ private readonly DispatcherTimer _updateTimer;
+
+ private bool _isModuleFilterEnabled;
+ private DataModelPropertiesViewModel? _mainDataModel;
+ private string? _propertySearch;
+ private Module? _selectedModule;
+ private bool _slowUpdates;
+
+ public DataModelDebugViewModel(IScreen hostScreen, IDataModelUIService dataModelUIService, IPluginManagementService pluginManagementService)
{
- private readonly IDataModelUIService _dataModelUIService;
- private readonly IPluginManagementService _pluginManagementService;
- private readonly Timer _updateTimer;
+ _dataModelUIService = dataModelUIService;
+ _pluginManagementService = pluginManagementService;
+ _updateTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(25), DispatcherPriority.Normal, (_, _) => Update());
- private bool _isModuleFilterEnabled;
- private DataModelPropertiesViewModel? _mainDataModel;
- private string? _propertySearch;
- private Module? _selectedModule;
- private bool _slowUpdates;
-
- public DataModelDebugViewModel(IScreen hostScreen, IDataModelUIService dataModelUIService, IPluginManagementService pluginManagementService)
+ HostScreen = hostScreen;
+ Modules = new ObservableCollection();
+
+ this.WhenActivated(disposables =>
{
- _dataModelUIService = dataModelUIService;
- _pluginManagementService = pluginManagementService;
- _updateTimer = new Timer(25);
- _updateTimer.Elapsed += UpdateTimerOnElapsed;
+ Observable.FromEventPattern(x => pluginManagementService.PluginFeatureEnabled += x, x => pluginManagementService.PluginFeatureEnabled -= x)
+ .Subscribe(d => PluginFeatureToggled(d.EventArgs.PluginFeature))
+ .DisposeWith(disposables);
+ Observable.FromEventPattern(x => pluginManagementService.PluginFeatureDisabled += x, x => pluginManagementService.PluginFeatureDisabled -= x)
+ .Subscribe(d => PluginFeatureToggled(d.EventArgs.PluginFeature))
+ .DisposeWith(disposables);
- HostScreen = hostScreen;
- Modules = new ObservableCollection();
+ GetDataModel();
+ _updateTimer.Start();
+ Disposable.Create(() => _updateTimer.Stop()).DisposeWith(disposables);
+ });
+ }
- this.WhenActivated(disposables =>
- {
- Observable.FromEventPattern(x => pluginManagementService.PluginFeatureEnabled += x, x => pluginManagementService.PluginFeatureEnabled -= x)
- .Subscribe(d => PluginFeatureToggled(d.EventArgs.PluginFeature))
- .DisposeWith(disposables);
- Observable.FromEventPattern(x => pluginManagementService.PluginFeatureDisabled += x, x => pluginManagementService.PluginFeatureDisabled -= x)
- .Subscribe(d => PluginFeatureToggled(d.EventArgs.PluginFeature))
- .DisposeWith(disposables);
+ public DataModelPropertiesViewModel? MainDataModel
+ {
+ get => _mainDataModel;
+ set => RaiseAndSetIfChanged(ref _mainDataModel, value);
+ }
- GetDataModel();
- _updateTimer.Start();
- Disposable.Create(() => _updateTimer.Stop()).DisposeWith(disposables);
- });
- }
+ public string? PropertySearch
+ {
+ get => _propertySearch;
+ set => RaiseAndSetIfChanged(ref _propertySearch, value);
+ }
- public string UrlPathSegment => "data-model";
- public IScreen HostScreen { get; }
-
- public DataModelPropertiesViewModel? MainDataModel
+ public bool SlowUpdates
+ {
+ get => _slowUpdates;
+ set
{
- get => _mainDataModel;
- set => RaiseAndSetIfChanged(ref _mainDataModel, value);
- }
-
- public string? PropertySearch
- {
- get => _propertySearch;
- set => RaiseAndSetIfChanged(ref _propertySearch, value);
- }
-
- public bool SlowUpdates
- {
- get => _slowUpdates;
- set
- {
- RaiseAndSetIfChanged(ref _slowUpdates, value);
- _updateTimer.Interval = _slowUpdates ? 500 : 25;
- }
- }
-
- public ObservableCollection Modules { get; }
-
- public Module? SelectedModule
- {
- get => _selectedModule;
- set
- {
- RaiseAndSetIfChanged(ref _selectedModule, value);
- GetDataModel();
- }
- }
-
- public bool IsModuleFilterEnabled
- {
- get => _isModuleFilterEnabled;
- set
- {
- RaiseAndSetIfChanged(ref _isModuleFilterEnabled, value);
-
- if (!IsModuleFilterEnabled)
- SelectedModule = null;
- else
- GetDataModel();
- }
- }
- private void UpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
- {
- if (MainDataModel == null)
- return;
-
- lock (MainDataModel)
- {
- MainDataModel.Update(_dataModelUIService, new DataModelUpdateConfiguration(true));
- }
- }
-
- private void PluginFeatureToggled(PluginFeature pluginFeature)
- {
- if (pluginFeature is Module)
- PopulateModules();
- }
-
- private void GetDataModel()
- {
- MainDataModel = SelectedModule != null
- ? _dataModelUIService.GetPluginDataModelVisualization(new List() { SelectedModule }, false)
- : _dataModelUIService.GetMainDataModelVisualization();
- }
-
- private void PopulateModules()
- {
- Modules.Clear();
- Modules.AddRange(_pluginManagementService.GetFeaturesOfType().Where(p => p.IsEnabled).OrderBy(m => m.Info.Name));
-
- if (MainDataModel == null)
- return;
-
- if (SelectedModule == null)
- {
- if (MainDataModel != null)
- _dataModelUIService.UpdateModules(MainDataModel);
- }
- else if (!SelectedModule.IsEnabled)
- SelectedModule = null;
+ RaiseAndSetIfChanged(ref _slowUpdates, value);
+ _updateTimer.Interval = TimeSpan.FromMilliseconds(_slowUpdates ? 500 : 25);
}
}
+
+ public ObservableCollection Modules { get; }
+
+ public Module? SelectedModule
+ {
+ get => _selectedModule;
+ set
+ {
+ RaiseAndSetIfChanged(ref _selectedModule, value);
+ GetDataModel();
+ }
+ }
+
+ public bool IsModuleFilterEnabled
+ {
+ get => _isModuleFilterEnabled;
+ set
+ {
+ RaiseAndSetIfChanged(ref _isModuleFilterEnabled, value);
+
+ if (!IsModuleFilterEnabled)
+ SelectedModule = null;
+ else
+ GetDataModel();
+ }
+ }
+
+ private void Update()
+ {
+ if (MainDataModel == null)
+ return;
+
+ lock (MainDataModel)
+ {
+ MainDataModel.Update(_dataModelUIService, new DataModelUpdateConfiguration(true));
+ }
+ }
+
+ private void PluginFeatureToggled(PluginFeature pluginFeature)
+ {
+ if (pluginFeature is Module)
+ PopulateModules();
+ }
+
+ private void GetDataModel()
+ {
+ MainDataModel = SelectedModule != null
+ ? _dataModelUIService.GetPluginDataModelVisualization(new List {SelectedModule}, false)
+ : _dataModelUIService.GetMainDataModelVisualization();
+ }
+
+ private void PopulateModules()
+ {
+ Modules.Clear();
+ Modules.AddRange(_pluginManagementService.GetFeaturesOfType().Where(p => p.IsEnabled).OrderBy(m => m.Info.Name));
+
+ if (MainDataModel == null)
+ return;
+
+ if (SelectedModule == null)
+ {
+ if (MainDataModel != null)
+ _dataModelUIService.UpdateModules(MainDataModel);
+ }
+ else if (!SelectedModule.IsEnabled)
+ {
+ SelectedModule = null;
+ }
+ }
+
+ public string UrlPathSegment => "data-model";
+ public IScreen HostScreen { get; }
}
\ No newline at end of file
diff --git a/src/Avalonia/Artemis.UI/Screens/Debugger/Tabs/Performance/PerformanceDebugViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Debugger/Tabs/Performance/PerformanceDebugViewModel.cs
index d4e81011b..a3b4c6ead 100644
--- a/src/Avalonia/Artemis.UI/Screens/Debugger/Tabs/Performance/PerformanceDebugViewModel.cs
+++ b/src/Avalonia/Artemis.UI/Screens/Debugger/Tabs/Performance/PerformanceDebugViewModel.cs
@@ -3,129 +3,128 @@ 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
{
- 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;
+ private int _renderWidth;
+
+ public PerformanceDebugViewModel(IScreen hostScreen, ICoreService coreService, IPluginManagementService pluginManagementService)
{
- private readonly ICoreService _coreService;
- private readonly IPluginManagementService _pluginManagementService;
- private double _currentFps;
- private string? _renderer;
- private int _renderHeight;
- private int _renderWidth;
+ HostScreen = hostScreen;
+ _coreService = coreService;
+ _pluginManagementService = pluginManagementService;
+ _updateTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(500), DispatcherPriority.Normal, (_, _) => Update());
- public PerformanceDebugViewModel(IScreen hostScreen, ICoreService coreService, IPluginManagementService pluginManagementService)
+ this.WhenActivated(disposables =>
{
- HostScreen = hostScreen;
- _coreService = coreService;
- _pluginManagementService = pluginManagementService;
+ Observable.FromEventPattern(x => pluginManagementService.PluginFeatureEnabled += x, x => pluginManagementService.PluginFeatureEnabled -= x)
+ .Subscribe(_ => Repopulate())
+ .DisposeWith(disposables);
+ Observable.FromEventPattern(x => pluginManagementService.PluginFeatureDisabled += x, x => pluginManagementService.PluginFeatureDisabled -= x)
+ .Subscribe(_ => Repopulate())
+ .DisposeWith(disposables);
+ Observable.FromEventPattern(x => pluginManagementService.PluginEnabled += x, x => pluginManagementService.PluginEnabled -= x)
+ .Subscribe(_ => Repopulate())
+ .DisposeWith(disposables);
+ Observable.FromEventPattern(x => pluginManagementService.PluginDisabled += x, x => pluginManagementService.PluginDisabled -= x)
+ .Subscribe(_ => Repopulate())
+ .DisposeWith(disposables);
- Timer updateTimer = new(500);
- updateTimer.Elapsed += UpdateTimerOnElapsed;
-
- this.WhenActivated(disposables =>
- {
- Observable.FromEventPattern(x => pluginManagementService.PluginFeatureEnabled += x, x => pluginManagementService.PluginFeatureEnabled -= x)
- .Subscribe(_ => Repopulate())
- .DisposeWith(disposables);
- Observable.FromEventPattern(x => pluginManagementService.PluginFeatureDisabled += x, x => pluginManagementService.PluginFeatureDisabled -= x)
- .Subscribe(_ => Repopulate())
- .DisposeWith(disposables);
- Observable.FromEventPattern(x => pluginManagementService.PluginEnabled += x, x => pluginManagementService.PluginEnabled -= x)
- .Subscribe(_ => Repopulate())
- .DisposeWith(disposables);
- Observable.FromEventPattern(x => pluginManagementService.PluginDisabled += x, x => pluginManagementService.PluginDisabled -= x)
- .Subscribe(_ => Repopulate())
- .DisposeWith(disposables);
-
- HandleActivation();
- PopulateItems();
- updateTimer.Start();
-
- Disposable.Create(() =>
- {
- updateTimer.Stop();
- Items.Clear();
- HandleDeactivation();
- }).DisposeWith(disposables);
- });
- }
-
-
- public string UrlPathSegment => "performance";
- public IScreen HostScreen { get; }
- public ObservableCollection Items { get; } = new();
-
- public double CurrentFps
- {
- get => _currentFps;
- set => RaiseAndSetIfChanged(ref _currentFps, value);
- }
-
- public int RenderWidth
- {
- get => _renderWidth;
- set => RaiseAndSetIfChanged(ref _renderWidth, value);
- }
-
- public int RenderHeight
- {
- get => _renderHeight;
- set => RaiseAndSetIfChanged(ref _renderHeight, value);
- }
-
- public string? Renderer
- {
- get => _renderer;
- set => RaiseAndSetIfChanged(ref _renderer, value);
- }
-
- private void HandleActivation()
- {
- Renderer = Constants.ManagedGraphicsContext != null ? Constants.ManagedGraphicsContext.GetType().Name : "Software";
- _coreService.FrameRendered += CoreServiceOnFrameRendered;
- }
-
- private void HandleDeactivation()
- {
- _coreService.FrameRendered -= CoreServiceOnFrameRendered;
- }
-
- private void PopulateItems()
- {
- foreach (PerformanceDebugPluginViewModel performanceDebugPluginViewModel in _pluginManagementService.GetAllPlugins()
- .Where(p => p.IsEnabled && p.Profilers.Any(pr => pr.Measurements.Any()))
- .OrderBy(p => p.Info.Name)
- .Select(p => new PerformanceDebugPluginViewModel(p)))
- Items.Add(performanceDebugPluginViewModel);
- }
-
- private void Repopulate()
- {
- Items.Clear();
+ HandleActivation();
PopulateItems();
- }
+ _updateTimer.Start();
- private void UpdateTimerOnElapsed(object? sender, ElapsedEventArgs e)
- {
- foreach (PerformanceDebugPluginViewModel viewModel in Items)
- viewModel.Update();
- }
-
- private void CoreServiceOnFrameRendered(object? sender, FrameRenderedEventArgs e)
- {
- CurrentFps = _coreService.FrameRate;
- SKImageInfo bitmapInfo = e.Texture.ImageInfo;
-
- RenderHeight = bitmapInfo.Height;
- RenderWidth = bitmapInfo.Width;
- }
+ Disposable.Create(() =>
+ {
+ _updateTimer.Stop();
+ Items.Clear();
+ HandleDeactivation();
+ }).DisposeWith(disposables);
+ });
}
+
+ public ObservableCollection Items { get; } = new();
+
+ public double CurrentFps
+ {
+ get => _currentFps;
+ set => RaiseAndSetIfChanged(ref _currentFps, value);
+ }
+
+ public int RenderWidth
+ {
+ get => _renderWidth;
+ set => RaiseAndSetIfChanged(ref _renderWidth, value);
+ }
+
+ public int RenderHeight
+ {
+ get => _renderHeight;
+ set => RaiseAndSetIfChanged(ref _renderHeight, value);
+ }
+
+ public string? Renderer
+ {
+ get => _renderer;
+ set => RaiseAndSetIfChanged(ref _renderer, value);
+ }
+
+ private void HandleActivation()
+ {
+ Renderer = Constants.ManagedGraphicsContext != null ? Constants.ManagedGraphicsContext.GetType().Name : "Software";
+ _coreService.FrameRendered += CoreServiceOnFrameRendered;
+ }
+
+ private void HandleDeactivation()
+ {
+ _coreService.FrameRendered -= CoreServiceOnFrameRendered;
+ }
+
+ private void PopulateItems()
+ {
+ foreach (PerformanceDebugPluginViewModel performanceDebugPluginViewModel in _pluginManagementService.GetAllPlugins()
+ .Where(p => p.IsEnabled && p.Profilers.Any(pr => pr.Measurements.Any()))
+ .OrderBy(p => p.Info.Name)
+ .Select(p => new PerformanceDebugPluginViewModel(p)))
+ Items.Add(performanceDebugPluginViewModel);
+ }
+
+ private void Repopulate()
+ {
+ Items.Clear();
+ PopulateItems();
+ }
+
+ private void Update()
+ {
+ foreach (PerformanceDebugPluginViewModel viewModel in Items)
+ viewModel.Update();
+ }
+
+ private void CoreServiceOnFrameRendered(object? sender, FrameRenderedEventArgs e)
+ {
+ CurrentFps = _coreService.FrameRate;
+ SKImageInfo bitmapInfo = e.Texture.ImageInfo;
+
+ RenderHeight = bitmapInfo.Height;
+ RenderWidth = bitmapInfo.Width;
+ }
+
+
+ public string UrlPathSegment => "performance";
+ public IScreen HostScreen { get; }
}
\ No newline at end of file
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/CableView.axaml b/src/Avalonia/Artemis.UI/Screens/VisualScripting/CableView.axaml
new file mode 100644
index 000000000..12b214fb4
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/CableView.axaml
@@ -0,0 +1,8 @@
+
+ Welcome to Avalonia!
+
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/CableView.axaml.cs b/src/Avalonia/Artemis.UI/Screens/VisualScripting/CableView.axaml.cs
new file mode 100644
index 000000000..b4fa8c6a4
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/CableView.axaml.cs
@@ -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
+ {
+ public CableView()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/CableViewModel.cs b/src/Avalonia/Artemis.UI/Screens/VisualScripting/CableViewModel.cs
new file mode 100644
index 000000000..9d14d845d
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/CableViewModel.cs
@@ -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);
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodePickerView.axaml b/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodePickerView.axaml
new file mode 100644
index 000000000..329c64a18
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodePickerView.axaml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+ Test
+
+
\ No newline at end of file
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodePickerView.axaml.cs b/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodePickerView.axaml.cs
new file mode 100644
index 000000000..921da0c83
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodePickerView.axaml.cs
@@ -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
+ {
+ public NodePickerView()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodePickerViewModel.cs b/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodePickerViewModel.cs
new file mode 100644
index 000000000..231463b3b
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodePickerViewModel.cs
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodeScriptView.axaml b/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodeScriptView.axaml
new file mode 100644
index 000000000..2f0c9114b
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodeScriptView.axaml
@@ -0,0 +1,19 @@
+
+
+
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodeScriptView.axaml.cs b/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodeScriptView.axaml.cs
new file mode 100644
index 000000000..8f5e65282
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodeScriptView.axaml.cs
@@ -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
+ {
+ 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();
+ }
+ }
+}
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodeScriptViewModel.cs b/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodeScriptViewModel.cs
new file mode 100644
index 000000000..e584407bc
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodeScriptViewModel.cs
@@ -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>(x => NodeScript.NodeAdded += x, x => NodeScript.NodeAdded -= x)
+ .Subscribe(e => HandleNodeAdded(e.EventArgs))
+ .DisposeWith(d);
+ Observable.FromEventPattern>(x => NodeScript.NodeRemoved += x, x => NodeScript.NodeRemoved -= x)
+ .Subscribe(e => HandleNodeRemoved(e.EventArgs))
+ .DisposeWith(d);
+ });
+
+ NodeViewModels = new ObservableCollection();
+ foreach (INode nodeScriptNode in NodeScript.Nodes)
+ NodeViewModels.Add(_nodeVmFactory.NodeViewModel(nodeScriptNode));
+ }
+
+ public NodeScript NodeScript { get; }
+ public ObservableCollection NodeViewModels { get; }
+ public NodePickerViewModel NodePickerViewModel { get; }
+
+
+ private void HandleNodeAdded(SingleValueEventArgs eventArgs)
+ {
+ NodeViewModels.Add(_nodeVmFactory.NodeViewModel(eventArgs.Value));
+ }
+
+ private void HandleNodeRemoved(SingleValueEventArgs 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();
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodeView.axaml b/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodeView.axaml
new file mode 100644
index 000000000..37cfb4264
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodeView.axaml
@@ -0,0 +1,8 @@
+
+ Welcome to Avalonia!
+
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodeView.axaml.cs b/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodeView.axaml.cs
new file mode 100644
index 000000000..7d8e70086
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodeView.axaml.cs
@@ -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
+ {
+ public NodeView()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodeViewModel.cs b/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodeViewModel.cs
new file mode 100644
index 000000000..e38eed9ed
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/NodeViewModel.cs
@@ -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);
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/InputPinCollectionView.axaml b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/InputPinCollectionView.axaml
new file mode 100644
index 000000000..3bf8ae477
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/InputPinCollectionView.axaml
@@ -0,0 +1,8 @@
+
+ Welcome to Avalonia!
+
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/InputPinCollectionView.axaml.cs b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/InputPinCollectionView.axaml.cs
new file mode 100644
index 000000000..bbde24ba6
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/InputPinCollectionView.axaml.cs
@@ -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
+ {
+ public InputPinCollectionView()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/InputPinCollectionViewModel.cs b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/InputPinCollectionViewModel.cs
new file mode 100644
index 000000000..75488b75e
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/InputPinCollectionViewModel.cs
@@ -0,0 +1,13 @@
+using Artemis.Core;
+
+namespace Artemis.UI.Screens.VisualScripting.Pins;
+
+public class InputPinCollectionViewModel : PinCollectionViewModel
+{
+ public InputPinCollection InputPinCollection { get; }
+
+ public InputPinCollectionViewModel(InputPinCollection inputPinCollection) : base(inputPinCollection)
+ {
+ InputPinCollection = inputPinCollection;
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/InputPinView.axaml b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/InputPinView.axaml
new file mode 100644
index 000000000..ae1b6724d
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/InputPinView.axaml
@@ -0,0 +1,8 @@
+
+ Welcome to Avalonia!
+
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/InputPinView.axaml.cs b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/InputPinView.axaml.cs
new file mode 100644
index 000000000..44a65692c
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/InputPinView.axaml.cs
@@ -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
+ {
+ public InputPinView()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/InputPinViewModel.cs b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/InputPinViewModel.cs
new file mode 100644
index 000000000..ed148b458
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/InputPinViewModel.cs
@@ -0,0 +1,13 @@
+using Artemis.Core;
+
+namespace Artemis.UI.Screens.VisualScripting.Pins;
+
+public class InputPinViewModel : PinViewModel
+{
+ public InputPin InputPin { get; }
+
+ public InputPinViewModel(InputPin inputPin) : base(inputPin)
+ {
+ InputPin = inputPin;
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/OutputPinCollectionView.axaml b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/OutputPinCollectionView.axaml
new file mode 100644
index 000000000..424369776
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/OutputPinCollectionView.axaml
@@ -0,0 +1,8 @@
+
+ Welcome to Avalonia!
+
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/OutputPinCollectionView.axaml.cs b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/OutputPinCollectionView.axaml.cs
new file mode 100644
index 000000000..ff6401ee8
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/OutputPinCollectionView.axaml.cs
@@ -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
+ {
+ public OutputPinCollectionView()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/OutputPinCollectionViewModel.cs b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/OutputPinCollectionViewModel.cs
new file mode 100644
index 000000000..7d8400019
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/OutputPinCollectionViewModel.cs
@@ -0,0 +1,13 @@
+using Artemis.Core;
+
+namespace Artemis.UI.Screens.VisualScripting.Pins;
+
+public class OutputPinCollectionViewModel : PinCollectionViewModel
+{
+ public OutputPinCollection OutputPinCollection { get; }
+
+ public OutputPinCollectionViewModel(OutputPinCollection outputPinCollection) : base(outputPinCollection)
+ {
+ OutputPinCollection = outputPinCollection;
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/OutputPinView.axaml b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/OutputPinView.axaml
new file mode 100644
index 000000000..893ff48e2
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/OutputPinView.axaml
@@ -0,0 +1,8 @@
+
+ Welcome to Avalonia!
+
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/OutputPinView.axaml.cs b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/OutputPinView.axaml.cs
new file mode 100644
index 000000000..175d9bd0d
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/OutputPinView.axaml.cs
@@ -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
+ {
+ public OutputPinView()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/OutputPinViewModel.cs b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/OutputPinViewModel.cs
new file mode 100644
index 000000000..0719fdcab
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/OutputPinViewModel.cs
@@ -0,0 +1,13 @@
+using Artemis.Core;
+
+namespace Artemis.UI.Screens.VisualScripting.Pins;
+
+public class OutputPinViewModel : PinViewModel
+{
+ public OutputPin OutputPin { get; }
+
+ public OutputPinViewModel(OutputPin outputPin) : base(outputPin)
+ {
+ OutputPin = outputPin;
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/PinCollectionViewModel.cs b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/PinCollectionViewModel.cs
new file mode 100644
index 000000000..42140ec4f
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/PinCollectionViewModel.cs
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/PinViewModel.cs b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/PinViewModel.cs
new file mode 100644
index 000000000..ec321632a
--- /dev/null
+++ b/src/Avalonia/Artemis.UI/Screens/VisualScripting/Pins/PinViewModel.cs
@@ -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; }
+}
\ No newline at end of file