diff --git a/src/Artemis.Core/Plugins/Modules/DataModel.cs b/src/Artemis.Core/Plugins/Modules/DataModel.cs
index 510883b39..ea1cacd6d 100644
--- a/src/Artemis.Core/Plugins/Modules/DataModel.cs
+++ b/src/Artemis.Core/Plugins/Modules/DataModel.cs
@@ -285,34 +285,43 @@ namespace Artemis.Core.Modules
internal bool IsPropertyInUse(string path, bool includeChildren)
{
path = path.ToUpperInvariant();
- return includeChildren
- ? _activePathsHashSet.Any(p => p.StartsWith(path, StringComparison.Ordinal))
- : _activePathsHashSet.Contains(path);
+ lock (_activePaths)
+ {
+ return includeChildren
+ ? _activePathsHashSet.Any(p => p.StartsWith(path, StringComparison.Ordinal))
+ : _activePathsHashSet.Contains(path);
+ }
}
internal void AddDataModelPath(DataModelPath path)
{
- if (_activePaths.Contains(path))
- return;
+ lock (_activePaths)
+ {
+ if (_activePaths.Contains(path))
+ return;
- _activePaths.Add(path);
+ _activePaths.Add(path);
- // Add to the hashset if this is the first path pointing
- string hashPath = path.Path.ToUpperInvariant();
- if (!_activePathsHashSet.Contains(hashPath))
- _activePathsHashSet.Add(hashPath);
+ // Add to the hashset if this is the first path pointing
+ string hashPath = path.Path.ToUpperInvariant();
+ if (!_activePathsHashSet.Contains(hashPath))
+ _activePathsHashSet.Add(hashPath);
+ }
OnActivePathAdded(new DataModelPathEventArgs(path));
}
internal void RemoveDataModelPath(DataModelPath path)
{
- if (!_activePaths.Remove(path))
- return;
+ lock (_activePaths)
+ {
+ if (!_activePaths.Remove(path))
+ return;
- // Remove from the hashset if this was the last path pointing there
- if (_activePaths.All(p => p.Path != path.Path))
- _activePathsHashSet.Remove(path.Path.ToUpperInvariant());
+ // Remove from the hashset if this was the last path pointing there
+ if (_activePaths.All(p => p.Path != path.Path))
+ _activePathsHashSet.Remove(path.Path.ToUpperInvariant());
+ }
OnActivePathRemoved(new DataModelPathEventArgs(path));
}
diff --git a/src/Artemis.Core/Services/PluginManagementService.cs b/src/Artemis.Core/Services/PluginManagementService.cs
index f56d20fb7..61d965cdc 100644
--- a/src/Artemis.Core/Services/PluginManagementService.cs
+++ b/src/Artemis.Core/Services/PluginManagementService.cs
@@ -634,7 +634,7 @@ namespace Artemis.Core.Services
if (isAutoEnable)
{
// Schedule a retry based on the amount of attempts
- if (pluginFeature.AutoEnableAttempts < 4)
+ if (pluginFeature.AutoEnableAttempts < 4 && pluginFeature.Plugin.IsEnabled)
{
TimeSpan retryDelay = TimeSpan.FromSeconds(pluginFeature.AutoEnableAttempts * 10);
_logger.Warning(
diff --git a/src/Artemis.Core/VisualScripting/Node.cs b/src/Artemis.Core/VisualScripting/Node.cs
index c81d54ade..a9bdfb8f2 100644
--- a/src/Artemis.Core/VisualScripting/Node.cs
+++ b/src/Artemis.Core/VisualScripting/Node.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+using Artemis.Core.Properties;
using Ninject;
using Ninject.Parameters;
diff --git a/src/Artemis.UI.Shared/NullCommand.cs b/src/Artemis.UI.Shared/NullCommand.cs
new file mode 100644
index 000000000..06e23ebbf
--- /dev/null
+++ b/src/Artemis.UI.Shared/NullCommand.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Windows.Input;
+
+namespace Artemis.UI.Shared;
+
+///
+/// Represents a placeholder command that does nothing and can't be executed.
+///
+public class NullCommand : ICommand
+{
+ private static readonly Lazy _instance = new(() => new NullCommand());
+
+ private NullCommand()
+ {
+ }
+
+ ///
+ /// Gets the static instance of this command.
+ ///
+ public static ICommand Instance => _instance.Value;
+
+ ///
+ public event EventHandler? CanExecuteChanged;
+
+ ///
+ public void Execute(object? parameter)
+ {
+ throw new InvalidOperationException("NullCommand cannot be executed");
+ }
+
+ ///
+ public bool CanExecute(object? parameter)
+ {
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Styles/Artemis.axaml b/src/Artemis.UI.Shared/Styles/Artemis.axaml
index 19d356099..cf068ddac 100644
--- a/src/Artemis.UI.Shared/Styles/Artemis.axaml
+++ b/src/Artemis.UI.Shared/Styles/Artemis.axaml
@@ -1,24 +1,14 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/src/Artemis.UI.Shared/Styles/Controls/GradientPicker.axaml b/src/Artemis.UI.Shared/Styles/Controls/GradientPicker.axaml
index 4bde3cdaf..208eba1da 100644
--- a/src/Artemis.UI.Shared/Styles/Controls/GradientPicker.axaml
+++ b/src/Artemis.UI.Shared/Styles/Controls/GradientPicker.axaml
@@ -88,9 +88,9 @@
Background="{DynamicResource LightCheckerboardBrush}"
Margin="5 0">
-
+
-
-
-
-
-
-
-
-
-
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeViewModel.cs
index eac0c9789..0326d7775 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeViewModel.cs
@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
-using System.Reactive;
using System.Reactive.Disposables;
using Artemis.Core;
using Artemis.Core.Services;
@@ -11,87 +10,114 @@ using Artemis.UI.Shared.Services.Interfaces;
using Artemis.UI.Shared.Services.ProfileEditor;
using ReactiveUI;
-namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
+namespace Artemis.UI.Screens.ProfileEditor.ProfileTree;
+
+public class ProfileTreeViewModel : TreeItemViewModel
{
- public class ProfileTreeViewModel : TreeItemViewModel
+ private readonly IProfileEditorService _profileEditorService;
+ private TreeItemViewModel? _selectedChild;
+
+ public ProfileTreeViewModel(IWindowService windowService,
+ IProfileEditorService profileEditorService,
+ ILayerBrushService layerBrushService,
+ IProfileEditorVmFactory profileEditorVmFactory,
+ IRgbService rgbService)
+ : base(null, null, windowService, profileEditorService, rgbService, layerBrushService, profileEditorVmFactory)
{
- private TreeItemViewModel? _selectedChild;
-
- public ProfileTreeViewModel(IWindowService windowService,
- IProfileEditorService profileEditorService,
- ILayerBrushService layerBrushService,
- IProfileEditorVmFactory profileEditorVmFactory,
- IRgbService rgbService)
- : base(null, null, windowService, profileEditorService, rgbService, layerBrushService, profileEditorVmFactory)
+ _profileEditorService = profileEditorService;
+ this.WhenActivated(d =>
{
- this.WhenActivated(d =>
+ profileEditorService.ProfileConfiguration.WhereNotNull().Subscribe(configuration =>
{
- profileEditorService.ProfileConfiguration.WhereNotNull().Subscribe(configuration =>
+ if (configuration.Profile == null)
{
- if (configuration.Profile == null)
- {
- windowService.ShowConfirmContentDialog("Failed to load profile", "It appears that this profile is corrupt and cannot be loaded. Please check your logs.", "Confirm", null);
- return;
- }
+ windowService.ShowConfirmContentDialog("Failed to load profile", "It appears that this profile is corrupt and cannot be loaded. Please check your logs.", "Confirm", null);
+ return;
+ }
- ProfileElement = configuration.Profile.GetRootFolder();
- SubscribeToProfileElement(d);
- CreateTreeItems();
- }).DisposeWith(d);
+ ProfileElement = configuration.Profile.GetRootFolder();
+ SubscribeToProfileElement(d);
+ CreateTreeItems();
+ }).DisposeWith(d);
- profileEditorService.ProfileElement.Subscribe(SelectCurrentProfileElement).DisposeWith(d);
- });
+ profileEditorService.ProfileElement.Subscribe(SelectCurrentProfileElement).DisposeWith(d);
+ });
- this.WhenAnyValue(vm => vm.SelectedChild).Subscribe(model =>
- {
- if (model?.ProfileElement is RenderProfileElement renderProfileElement)
- profileEditorService.ChangeCurrentProfileElement(renderProfileElement);
- });
-
- ClearSelection = ReactiveCommand.Create(() => profileEditorService.ChangeCurrentProfileElement(null));
- }
-
- public ReactiveCommand ClearSelection { get; }
-
- public TreeItemViewModel? SelectedChild
+ this.WhenAnyValue(vm => vm.SelectedChild).Subscribe(model =>
{
- get => _selectedChild;
- set => RaiseAndSetIfChanged(ref _selectedChild, value);
- }
+ if (model?.ProfileElement is RenderProfileElement renderProfileElement)
+ profileEditorService.ChangeCurrentProfileElement(renderProfileElement);
+ });
+ }
- private void SelectCurrentProfileElement(RenderProfileElement? element)
+ public TreeItemViewModel? SelectedChild
+ {
+ get => _selectedChild;
+ set => RaiseAndSetIfChanged(ref _selectedChild, value);
+ }
+
+ public override bool SupportsChildren => true;
+
+ public void ClearSelection()
+ {
+ _profileEditorService.ChangeCurrentProfileElement(null);
+ }
+
+ public void RenameSelected()
+ {
+ SelectedChild?.Rename.Execute().Subscribe();
+ }
+
+ public void DeleteSelected()
+ {
+ SelectedChild?.Delete.Execute().Subscribe();
+ }
+
+ public void DuplicateSelected()
+ {
+ SelectedChild?.Duplicate.Execute().Subscribe();
+ }
+
+ public void CopySelected()
+ {
+ SelectedChild?.Copy.Execute().Subscribe();
+ }
+
+ public void PasteSelected()
+ {
+ SelectedChild?.Paste.Execute().Subscribe();
+ }
+
+ private void SelectCurrentProfileElement(RenderProfileElement? element)
+ {
+ if (SelectedChild?.ProfileElement == element)
+ return;
+
+ // Find the tree item belonging to the selected element
+ List treeItems = GetAllTreeItems(Children);
+ TreeItemViewModel? selected = treeItems.FirstOrDefault(e => e.ProfileElement == element);
+
+ // Walk up the tree, expanding parents
+ TreeItemViewModel? currentParent = selected?.Parent;
+ while (currentParent != null)
{
- if (SelectedChild?.ProfileElement == element)
- return;
-
- // Find the tree item belonging to the selected element
- List treeItems = GetAllTreeItems(Children);
- TreeItemViewModel? selected = treeItems.FirstOrDefault(e => e.ProfileElement == element);
-
- // Walk up the tree, expanding parents
- TreeItemViewModel? currentParent = selected?.Parent;
- while (currentParent != null)
- {
- currentParent.IsExpanded = true;
- currentParent = currentParent.Parent;
- }
-
- SelectedChild = selected;
+ currentParent.IsExpanded = true;
+ currentParent = currentParent.Parent;
}
- private List GetAllTreeItems(ObservableCollection treeItems)
+ SelectedChild = selected;
+ }
+
+ private List GetAllTreeItems(ObservableCollection treeItems)
+ {
+ List result = new();
+ foreach (TreeItemViewModel treeItemViewModel in treeItems)
{
- List result = new();
- foreach (TreeItemViewModel treeItemViewModel in treeItems)
- {
- result.Add(treeItemViewModel);
- if (treeItemViewModel.Children.Any())
- result.AddRange(GetAllTreeItems(treeItemViewModel.Children));
- }
-
- return result;
+ result.Add(treeItemViewModel);
+ if (treeItemViewModel.Children.Any())
+ result.AddRange(GetAllTreeItems(treeItemViewModel.Children));
}
- public override bool SupportsChildren => true;
+ return result;
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertiesViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertiesViewModel.cs
index 67f4ee047..e2006f865 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertiesViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertiesViewModel.cs
@@ -57,7 +57,14 @@ public class PropertiesViewModel : ActivatableViewModelBase
{
_profileElement = profileEditorService.ProfileElement.ToProperty(this, vm => vm.ProfileElement).DisposeWith(d);
_pixelsPerSecond = profileEditorService.PixelsPerSecond.ToProperty(this, vm => vm.PixelsPerSecond).DisposeWith(d);
- Disposable.Create(() => _settingsService.SaveAllSettings()).DisposeWith(d);
+ Disposable.Create(() =>
+ {
+ _settingsService.SaveAllSettings();
+ foreach ((LayerPropertyGroup _, PropertyGroupViewModel value) in _cachedViewModels)
+ value.Dispose();
+ _cachedViewModels.Clear();
+
+ }).DisposeWith(d);
});
this.WhenAnyValue(vm => vm.ProfileElement).Subscribe(_ => UpdateGroups());
}
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelineGroupViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelineGroupViewModel.cs
index 87cac2175..76c010225 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelineGroupViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelineGroupViewModel.cs
@@ -23,7 +23,7 @@ public class TimelineGroupViewModel : ActivatableViewModelBase
{
_pixelsPerSecond = p;
UpdateKeyframePositions();
- });
+ }).DisposeWith(d);
this.WhenAnyValue(vm => vm.PropertyGroupViewModel.IsExpanded).Subscribe(_ => UpdateKeyframePositions()).DisposeWith(d);
PropertyGroupViewModel.WhenAnyValue(vm => vm.IsExpanded).Subscribe(_ => this.RaisePropertyChanged(nameof(Children))).DisposeWith(d);
});
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/StatusBar/StatusBarViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/StatusBar/StatusBarViewModel.cs
index 802c407a8..341aef64c 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/StatusBar/StatusBarViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/StatusBar/StatusBarViewModel.cs
@@ -24,7 +24,7 @@ public class StatusBarViewModel : ActivatableViewModelBase
{
_profileElement = profileEditorService.ProfileElement.ToProperty(this, vm => vm.ProfileElement).DisposeWith(d);
_history = profileEditorService.History.ToProperty(this, vm => vm.History).DisposeWith(d);
- _pixelsPerSecond = profileEditorService.PixelsPerSecond.ToProperty(this, vm => vm.PixelsPerSecond);
+ _pixelsPerSecond = profileEditorService.PixelsPerSecond.ToProperty(this, vm => vm.PixelsPerSecond).DisposeWith(d);
});
this.WhenAnyValue(vm => vm.History)
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/VisualEditorView.axaml b/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/VisualEditorView.axaml
index 1d7464a62..7aa8d3859 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/VisualEditorView.axaml
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/VisualEditorView.axaml
@@ -9,6 +9,18 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.VisualEditor.VisualEditorView"
x:DataType="visualEditor:VisualEditorViewModel">
+
+
+
+
+
+
+
diff --git a/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorTitleBarView.axaml.cs b/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorTitleBarView.axaml.cs
index 08b7a31c3..ae89f6723 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorTitleBarView.axaml.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorTitleBarView.axaml.cs
@@ -19,7 +19,6 @@ namespace Artemis.UI.Screens.ProfileEditor
private void MenuItem_OnSubmenuOpened(object? sender, RoutedEventArgs e)
{
-
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Root/RootViewModel.cs b/src/Artemis.UI/Screens/Root/RootViewModel.cs
index 7bb261274..0cdcc27b3 100644
--- a/src/Artemis.UI/Screens/Root/RootViewModel.cs
+++ b/src/Artemis.UI/Screens/Root/RootViewModel.cs
@@ -15,6 +15,7 @@ using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Platform;
using Avalonia.Threading;
+using JetBrains.Annotations;
using ReactiveUI;
namespace Artemis.UI.Screens.Root
@@ -95,6 +96,7 @@ namespace Artemis.UI.Screens.Root
{
_lifeTime.MainWindow = null;
SidebarViewModel = null;
+ Router.NavigateAndReset.Execute(new EmptyViewModel(this, "blank")).Subscribe();
OnMainWindowClosed();
}
@@ -220,4 +222,12 @@ namespace Artemis.UI.Screens.Root
#endregion
}
+
+ internal class EmptyViewModel : MainScreenViewModel
+ {
+ ///
+ public EmptyViewModel(IScreen hostScreen, string urlPathSegment) : base(hostScreen, urlPathSegment)
+ {
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Settings/Tabs/GeneralTabViewModel.cs b/src/Artemis.UI/Screens/Settings/Tabs/GeneralTabViewModel.cs
index d3c3c4ff8..6519d770a 100644
--- a/src/Artemis.UI/Screens/Settings/Tabs/GeneralTabViewModel.cs
+++ b/src/Artemis.UI/Screens/Settings/Tabs/GeneralTabViewModel.cs
@@ -4,6 +4,7 @@ using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Reactive;
+using System.Reactive.Disposables;
using System.Threading;
using System.Threading.Tasks;
using Artemis.Core;
@@ -30,8 +31,8 @@ namespace Artemis.UI.Screens.Settings
DisplayName = "General";
_settingsService = settingsService;
_debugService = debugService;
- _fluentAvaloniaTheme = AvaloniaLocator.Current.GetService(); ;
-
+ _fluentAvaloniaTheme = AvaloniaLocator.Current.GetService();
+
List layerBrushProviders = pluginManagementService.GetFeaturesOfType();
LayerBrushDescriptors = new ObservableCollection(layerBrushProviders.SelectMany(l => l.LayerBrushDescriptors));
_defaultLayerBrushDescriptor = _settingsService.GetSetting("ProfileEditor.DefaultLayerBrushDescriptor", new LayerBrushReference
@@ -45,8 +46,12 @@ namespace Artemis.UI.Screens.Settings
ShowSetupWizard = ReactiveCommand.Create(ExecuteShowSetupWizard);
ShowDebugger = ReactiveCommand.Create(ExecuteShowDebugger);
ShowDataFolder = ReactiveCommand.Create(ExecuteShowDataFolder);
-
- UIColorScheme.SettingChanged += UIColorSchemeOnSettingChanged;
+
+ this.WhenActivated(d =>
+ {
+ UIColorScheme.SettingChanged += UIColorSchemeOnSettingChanged;
+ Disposable.Create(() => UIColorScheme.SettingChanged -= UIColorSchemeOnSettingChanged).DisposeWith(d);
+ });
}
private void UIColorSchemeOnSettingChanged(object? sender, EventArgs e)
@@ -139,7 +144,7 @@ namespace Artemis.UI.Screens.Settings
}
#endregion
-
+
#region Updating
private Task ExecuteCheckForUpdate(CancellationToken cancellationToken)
diff --git a/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorView.axaml b/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorView.axaml
index 66f414663..c2c55e11c 100644
--- a/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorView.axaml
+++ b/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorView.axaml
@@ -7,7 +7,18 @@
xmlns:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.SurfaceEditor.SurfaceEditorView">
-
+
+
+
+
+
+
+
+ IsVisible="{CompiledBinding DisplayValue}">
diff --git a/src/Artemis.UI/Screens/VisualScripting/CableViewModel.cs b/src/Artemis.UI/Screens/VisualScripting/CableViewModel.cs
index 383388bb1..d4d8bba4c 100644
--- a/src/Artemis.UI/Screens/VisualScripting/CableViewModel.cs
+++ b/src/Artemis.UI/Screens/VisualScripting/CableViewModel.cs
@@ -15,14 +15,15 @@ namespace Artemis.UI.Screens.VisualScripting;
public class CableViewModel : ActivatableViewModelBase
{
- private readonly ObservableAsPropertyHelper _connected;
- private readonly ObservableAsPropertyHelper _fromPoint;
- private readonly ObservableAsPropertyHelper _toPoint;
- private readonly ObservableAsPropertyHelper _valuePoint;
private ObservableAsPropertyHelper? _cableColor;
+ private ObservableAsPropertyHelper? _fromPoint;
+ private ObservableAsPropertyHelper? _toPoint;
+ private ObservableAsPropertyHelper? _valuePoint;
+ private ObservableAsPropertyHelper? _connected;
private PinViewModel? _fromViewModel;
private PinViewModel? _toViewModel;
+ private bool _displayValue;
public CableViewModel(NodeScriptViewModel nodeScriptViewModel, IPin from, IPin to)
{
@@ -42,25 +43,32 @@ public class CableViewModel : ActivatableViewModelBase
.Switch()
.ToProperty(this, vm => vm.CableColor)
.DisposeWith(d);
+
+ _fromPoint = this.WhenAnyValue(vm => vm.FromViewModel)
+ .Select(p => p != null ? p.WhenAnyValue(pvm => pvm.Position) : Observable.Never())
+ .Switch()
+ .ToProperty(this, vm => vm.FromPoint)
+ .DisposeWith(d);
+ _toPoint = this.WhenAnyValue(vm => vm.ToViewModel)
+ .Select(p => p != null ? p.WhenAnyValue(pvm => pvm.Position) : Observable.Never())
+ .Switch()
+ .ToProperty(this, vm => vm.ToPoint)
+ .DisposeWith(d);
+ _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)
+ .DisposeWith(d);
+
+ // 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)
+ .DisposeWith(d);
});
- _fromPoint = this.WhenAnyValue(vm => vm.FromViewModel)
- .Select(p => p != null ? p.WhenAnyValue(pvm => pvm.Position) : Observable.Never())
- .Switch()
- .ToProperty(this, vm => vm.FromPoint);
- _toPoint = this.WhenAnyValue(vm => vm.ToViewModel)
- .Select(p => p != null ? p.WhenAnyValue(pvm => pvm.Position) : Observable.Never())
- .Switch()
- .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);
- // 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);
+ DisplayValue = !nodeScriptViewModel.IsPreview;
}
public PinViewModel? FromViewModel
@@ -75,10 +83,16 @@ public class CableViewModel : ActivatableViewModelBase
set => RaiseAndSetIfChanged(ref _toViewModel, value);
}
- public bool Connected => _connected.Value;
+ public bool DisplayValue
+ {
+ get => _displayValue;
+ set => _displayValue = value;
+ }
- public Point FromPoint => _fromPoint.Value;
- public Point ToPoint => _toPoint.Value;
- public Point ValuePoint => _valuePoint.Value;
+ public bool Connected => _connected?.Value ?? false;
+
+ public Point FromPoint => _fromPoint?.Value ?? new Point();
+ public Point ToPoint => _toPoint?.Value ?? new Point();
+ public Point ValuePoint => _valuePoint?.Value ?? new Point();
public Color CableColor => _cableColor?.Value ?? new Color(255, 255, 255, 255);
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/VisualScripting/NodePickerView.axaml b/src/Artemis.UI/Screens/VisualScripting/NodePickerView.axaml
index 1d5f3d0c3..db9c8adae 100644
--- a/src/Artemis.UI/Screens/VisualScripting/NodePickerView.axaml
+++ b/src/Artemis.UI/Screens/VisualScripting/NodePickerView.axaml
@@ -21,7 +21,7 @@
FontFamily="{StaticResource SymbolThemeFontFamily}"
Classes="AppBarButton"
Command="{Binding $parent[TextBox].Clear}"
- IsVisible="{Binding Text, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TextBox}}, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
+ IsVisible="{Binding $parent[TextBox].Text, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
+
-
+
-
-
-
-
+
-
diff --git a/src/Artemis.UI/Screens/VisualScripting/NodeScriptView.axaml.cs b/src/Artemis.UI/Screens/VisualScripting/NodeScriptView.axaml.cs
index 6a5414150..0651ed5a6 100644
--- a/src/Artemis.UI/Screens/VisualScripting/NodeScriptView.axaml.cs
+++ b/src/Artemis.UI/Screens/VisualScripting/NodeScriptView.axaml.cs
@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
+using System.Collections.Specialized;
using System.Linq;
using System.Reactive.Disposables;
+using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.Events;
using Artemis.UI.Shared.Controls;
@@ -15,6 +17,8 @@ using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.ReactiveUI;
+using Avalonia.Threading;
+using DynamicData.Binding;
using ReactiveUI;
namespace Artemis.UI.Screens.VisualScripting;
@@ -38,27 +42,17 @@ public class NodeScriptView : ReactiveUserControl
UpdateZoomBorderBackground();
_grid.AddHandler(PointerReleasedEvent, CanvasOnPointerReleased, RoutingStrategies.Direct | RoutingStrategies.Tunnel | RoutingStrategies.Bubble, true);
+
this.WhenActivated(d =>
{
- ViewModel!.PickerPositionSubject.Subscribe(p =>
- {
- ViewModel.NodePickerViewModel.Position = p;
- _grid?.ContextFlyout?.ShowAt(_grid, true);
- }).DisposeWith(d);
-
+ ViewModel!.PickerPositionSubject.Subscribe(ShowPickerAt).DisposeWith(d);
if (ViewModel.IsPreview)
{
BoundsProperty.Changed.Subscribe(BoundsPropertyChanged).DisposeWith(d);
- ViewModel.NodeScript.NodeAdded += NodesChanged;
- ViewModel.NodeScript.NodeRemoved += NodesChanged;
- Disposable.Create(() =>
- {
- ViewModel.NodeScript.NodeAdded -= NodesChanged;
- ViewModel.NodeScript.NodeRemoved -= NodesChanged;
- }).DisposeWith(d);
+ ViewModel.NodeViewModels.ToObservableChangeSet().Subscribe(_ => AutoFitIfPreview()).DisposeWith(d);
}
- AutoFit(true);
+ Dispatcher.UIThread.InvokeAsync(() => AutoFit(true), DispatcherPriority.ContextIdle);
});
}
@@ -68,17 +62,20 @@ public class NodeScriptView : ReactiveUserControl
return base.MeasureOverride(availableSize);
}
+ private void ShowPickerAt(Point point)
+ {
+ if (ViewModel == null)
+ return;
+ ViewModel.NodePickerViewModel.Position = point;
+ _grid?.ContextFlyout?.ShowAt(_grid, true);
+ }
+
private void AutoFitIfPreview()
{
if (ViewModel != null && ViewModel.IsPreview)
AutoFit(true);
}
- private void NodesChanged(object? sender, SingleValueEventArgs e)
- {
- AutoFitIfPreview();
- }
-
private void BoundsPropertyChanged(AvaloniaPropertyChangedEventArgs obj)
{
if (_nodesContainer.ItemContainerGenerator.Containers.Select(c => c.ContainerControl).Contains(obj.Sender))
@@ -102,8 +99,8 @@ public class NodeScriptView : ReactiveUserControl
double bottom = _nodesContainer.ItemContainerGenerator.Containers.Select(c => c.ContainerControl.Bounds.Bottom).Max();
double right = _nodesContainer.ItemContainerGenerator.Containers.Select(c => c.ContainerControl.Bounds.Right).Max();
- // Add a 5 pixel margin around the rect
- Rect scriptRect = new(new Point(left - 5, top - 5), new Point(right + 5, bottom + 5));
+ // Add a 10 pixel margin around the rect
+ Rect scriptRect = new(new Point(left - 10, top - 10), new Point(right + 10, bottom + 10));
// The scale depends on the available space
double scale = Math.Min(1, Math.Min(Bounds.Width / scriptRect.Width, Bounds.Height / scriptRect.Height));
diff --git a/src/Artemis.UI/Screens/VisualScripting/NodeView.axaml b/src/Artemis.UI/Screens/VisualScripting/NodeView.axaml
index 1d58b8175..aebe108cf 100644
--- a/src/Artemis.UI/Screens/VisualScripting/NodeView.axaml
+++ b/src/Artemis.UI/Screens/VisualScripting/NodeView.axaml
@@ -63,7 +63,10 @@
-
+
diff --git a/src/Artemis.UI/Screens/VisualScripting/NodeView.axaml.cs b/src/Artemis.UI/Screens/VisualScripting/NodeView.axaml.cs
index 66f1804f3..799e8227a 100644
--- a/src/Artemis.UI/Screens/VisualScripting/NodeView.axaml.cs
+++ b/src/Artemis.UI/Screens/VisualScripting/NodeView.axaml.cs
@@ -2,7 +2,10 @@ using System;
using System.Collections.Generic;
using Avalonia;
using Avalonia.Controls;
+using Avalonia.Controls.Mixins;
+using Avalonia.Controls.Presenters;
using Avalonia.Input;
+using Avalonia.LogicalTree;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
using Avalonia.VisualTree;
diff --git a/src/Artemis.UI/Screens/VisualScripting/NodeViewModel.cs b/src/Artemis.UI/Screens/VisualScripting/NodeViewModel.cs
index e26d17a2b..79dca9758 100644
--- a/src/Artemis.UI/Screens/VisualScripting/NodeViewModel.cs
+++ b/src/Artemis.UI/Screens/VisualScripting/NodeViewModel.cs
@@ -35,8 +35,8 @@ public class NodeViewModel : ActivatableViewModelBase
public NodeViewModel(NodeScriptViewModel nodeScriptViewModel, INode node, INodeVmFactory nodeVmFactory, INodeEditorService nodeEditorService)
{
- NodeScriptViewModel = nodeScriptViewModel;
_nodeEditorService = nodeEditorService;
+ NodeScriptViewModel = nodeScriptViewModel;
Node = node;
DeleteNode = ReactiveCommand.Create(ExecuteDeleteNode, this.WhenAnyValue(vm => vm.IsStaticNode).Select(v => !v));
diff --git a/src/Artemis.UI/Screens/VisualScripting/Pins/InputPinView.axaml b/src/Artemis.UI/Screens/VisualScripting/Pins/InputPinView.axaml
index 523457358..b995c00a1 100644
--- a/src/Artemis.UI/Screens/VisualScripting/Pins/InputPinView.axaml
+++ b/src/Artemis.UI/Screens/VisualScripting/Pins/InputPinView.axaml
@@ -3,28 +3,22 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:pins="clr-namespace:Artemis.UI.Screens.VisualScripting.Pins"
+ xmlns:converters="clr-namespace:Artemis.UI.Converters"
mc:Ignorable="d"
d:DesignWidth="200"
x:Class="Artemis.UI.Screens.VisualScripting.Pins.InputPinView"
x:DataType="pins:PinViewModel">
+
+
+
-
-
+
diff --git a/src/Artemis.UI/Screens/VisualScripting/Pins/OutputPinView.axaml b/src/Artemis.UI/Screens/VisualScripting/Pins/OutputPinView.axaml
index cb8e3d832..675b17520 100644
--- a/src/Artemis.UI/Screens/VisualScripting/Pins/OutputPinView.axaml
+++ b/src/Artemis.UI/Screens/VisualScripting/Pins/OutputPinView.axaml
@@ -3,29 +3,23 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:pins="clr-namespace:Artemis.UI.Screens.VisualScripting.Pins"
+ xmlns:converters="clr-namespace:Artemis.UI.Converters"
mc:Ignorable="d"
d:DesignWidth="200"
x:Class="Artemis.UI.Screens.VisualScripting.Pins.OutputPinView"
x:DataType="pins:PinViewModel">
+
+
+
-
-
+
\ No newline at end of file
diff --git a/src/Artemis.UI/ViewLocator.cs b/src/Artemis.UI/ViewLocator.cs
index 45ceec2d2..5205c004a 100644
--- a/src/Artemis.UI/ViewLocator.cs
+++ b/src/Artemis.UI/ViewLocator.cs
@@ -23,7 +23,11 @@ public class ViewLocator : IDataTemplate
throw new ArtemisUIException($"The views of activatable view models should inherit ReactiveUserControl, in this case ReactiveUserControl<{data.GetType().Name}>.");
if (type != null)
+ {
+ Debug.WriteLine("[ViewLocator] Creating instance of '{0}'", type);
return (Control) Activator.CreateInstance(type)!;
+ }
+
return new TextBlock {Text = "Not Found: " + name};
}