1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2026-01-01 18:23:32 +00:00

UI - Fixed a lot of binding errors

UI - Added most missing DisposeWith calls
This commit is contained in:
Robert 2022-04-02 01:10:06 +02:00
parent 8fd18b9565
commit 372991a69b
28 changed files with 347 additions and 230 deletions

View File

@ -285,34 +285,43 @@ namespace Artemis.Core.Modules
internal bool IsPropertyInUse(string path, bool includeChildren) internal bool IsPropertyInUse(string path, bool includeChildren)
{ {
path = path.ToUpperInvariant(); path = path.ToUpperInvariant();
return includeChildren lock (_activePaths)
? _activePathsHashSet.Any(p => p.StartsWith(path, StringComparison.Ordinal)) {
: _activePathsHashSet.Contains(path); return includeChildren
? _activePathsHashSet.Any(p => p.StartsWith(path, StringComparison.Ordinal))
: _activePathsHashSet.Contains(path);
}
} }
internal void AddDataModelPath(DataModelPath path) internal void AddDataModelPath(DataModelPath path)
{ {
if (_activePaths.Contains(path)) lock (_activePaths)
return; {
if (_activePaths.Contains(path))
return;
_activePaths.Add(path); _activePaths.Add(path);
// Add to the hashset if this is the first path pointing // Add to the hashset if this is the first path pointing
string hashPath = path.Path.ToUpperInvariant(); string hashPath = path.Path.ToUpperInvariant();
if (!_activePathsHashSet.Contains(hashPath)) if (!_activePathsHashSet.Contains(hashPath))
_activePathsHashSet.Add(hashPath); _activePathsHashSet.Add(hashPath);
}
OnActivePathAdded(new DataModelPathEventArgs(path)); OnActivePathAdded(new DataModelPathEventArgs(path));
} }
internal void RemoveDataModelPath(DataModelPath path) internal void RemoveDataModelPath(DataModelPath path)
{ {
if (!_activePaths.Remove(path)) lock (_activePaths)
return; {
if (!_activePaths.Remove(path))
return;
// Remove from the hashset if this was the last path pointing there // Remove from the hashset if this was the last path pointing there
if (_activePaths.All(p => p.Path != path.Path)) if (_activePaths.All(p => p.Path != path.Path))
_activePathsHashSet.Remove(path.Path.ToUpperInvariant()); _activePathsHashSet.Remove(path.Path.ToUpperInvariant());
}
OnActivePathRemoved(new DataModelPathEventArgs(path)); OnActivePathRemoved(new DataModelPathEventArgs(path));
} }

View File

@ -634,7 +634,7 @@ namespace Artemis.Core.Services
if (isAutoEnable) if (isAutoEnable)
{ {
// Schedule a retry based on the amount of attempts // 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); TimeSpan retryDelay = TimeSpan.FromSeconds(pluginFeature.AutoEnableAttempts * 10);
_logger.Warning( _logger.Warning(

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using Artemis.Core.Properties;
using Ninject; using Ninject;
using Ninject.Parameters; using Ninject.Parameters;

View File

@ -0,0 +1,36 @@
using System;
using System.Windows.Input;
namespace Artemis.UI.Shared;
/// <summary>
/// Represents a placeholder command that does nothing and can't be executed.
/// </summary>
public class NullCommand : ICommand
{
private static readonly Lazy<NullCommand> _instance = new(() => new NullCommand());
private NullCommand()
{
}
/// <summary>
/// Gets the static instance of this command.
/// </summary>
public static ICommand Instance => _instance.Value;
/// <inheritdoc />
public event EventHandler? CanExecuteChanged;
/// <inheritdoc />
public void Execute(object? parameter)
{
throw new InvalidOperationException("NullCommand cannot be executed");
}
/// <inheritdoc />
public bool CanExecute(object? parameter)
{
return false;
}
}

View File

@ -1,24 +1,14 @@
<Styles xmlns="https://github.com/avaloniaui" <Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Styles.Resources> <Styles.Resources>
<VisualBrush x:Key="CheckerboardBrush" TileMode="Tile" Stretch="Uniform" DestinationRect="0,0,15,15"> <VisualBrush x:Key="CheckerboardBrush" TileMode="Tile" Stretch="Uniform" DestinationRect="0,0,12,12">
<VisualBrush.Visual> <VisualBrush.Visual>
<Grid Width="15" Height="15" RowDefinitions="*,*" ColumnDefinitions="*,*"> <Canvas Width="12" Height="12">
<Rectangle Grid.Row="0" Grid.Column="0" Fill="Black" Opacity="0.15" /> <Rectangle Width="6" Height="6" Fill="Black" Opacity="0.15" />
<Rectangle Grid.Row="0" Grid.Column="1" /> <Rectangle Width="6" Height="6" Canvas.Left="6" />
<Rectangle Grid.Row="1" Grid.Column="0" /> <Rectangle Width="6" Height="6" Canvas.Top="6" />
<Rectangle Grid.Row="1" Grid.Column="1" Fill="Black" Opacity="0.15" /> <Rectangle Width="6" Height="6" Canvas.Left="6" Canvas.Top="6" Fill="Black" Opacity="0.15" />
</Grid> </Canvas>
</VisualBrush.Visual>
</VisualBrush>
<VisualBrush x:Key="LargeCheckerboardBrush" TileMode="Tile" Stretch="Uniform" SourceRect="0,0,25,25">
<VisualBrush.Visual>
<Grid Width="25" Height="25" RowDefinitions="*,*" ColumnDefinitions="*,*">
<Rectangle Grid.Row="0" Grid.Column="0" Fill="Black" Opacity="0.15" />
<Rectangle Grid.Row="0" Grid.Column="1" />
<Rectangle Grid.Row="1" Grid.Column="0" />
<Rectangle Grid.Row="1" Grid.Column="1" Fill="Black" Opacity="0.15" />
</Grid>
</VisualBrush.Visual> </VisualBrush.Visual>
</VisualBrush> </VisualBrush>
</Styles.Resources> </Styles.Resources>

View File

@ -88,9 +88,9 @@
Background="{DynamicResource LightCheckerboardBrush}" Background="{DynamicResource LightCheckerboardBrush}"
Margin="5 0"> Margin="5 0">
<Border Background="{TemplateBinding LinearGradientBrush}"> <Border Background="{TemplateBinding LinearGradientBrush}">
<ItemsControl Items="{TemplateBinding ColorGradient}" ClipToBounds="False"> <ItemsControl Name="GradientStops" Items="{TemplateBinding ColorGradient}" ClipToBounds="False">
<ItemsControl.Styles> <ItemsControl.Styles>
<Style Selector="ContentPresenter"> <Style Selector="ItemsControl#GradientStops > ContentPresenter">
<Setter Property="Canvas.Left"> <Setter Property="Canvas.Left">
<Setter.Value> <Setter.Value>
<MultiBinding Converter="{StaticResource WidthNormalizedConverter}"> <MultiBinding Converter="{StaticResource WidthNormalizedConverter}">

View File

@ -22,7 +22,7 @@ public class MenuBarViewModel : ActivatableViewModelBase
this.WhenActivated(d => this.WhenActivated(d =>
{ {
profileEditorService.History.Subscribe(history => History = history).DisposeWith(d); profileEditorService.History.Subscribe(history => History = history).DisposeWith(d);
_profileConfiguration = profileEditorService.ProfileConfiguration.ToProperty(this, vm => vm.ProfileConfiguration); _profileConfiguration = profileEditorService.ProfileConfiguration.ToProperty(this, vm => vm.ProfileConfiguration).DisposeWith(d);
_isSuspended = profileEditorService.ProfileConfiguration _isSuspended = profileEditorService.ProfileConfiguration
.Select(p => p?.WhenAnyValue(c => c.IsSuspended) ?? Observable.Never<bool>()) .Select(p => p?.WhenAnyValue(c => c.IsSuspended) ?? Observable.Never<bool>())
.Switch() .Switch()

View File

@ -10,6 +10,15 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.ProfileTree.ProfileTreeView" x:Class="Artemis.UI.Screens.ProfileEditor.ProfileTree.ProfileTreeView"
x:DataType="profileTree:ProfileTreeViewModel"> x:DataType="profileTree:ProfileTreeViewModel">
<!-- These cause binding errors, not my fault - https://github.com/AvaloniaUI/Avalonia/issues/5762 -->
<UserControl.KeyBindings>
<KeyBinding Gesture="Escape" Command="{Binding ClearSelection}" />
<KeyBinding Gesture="F2" Command="{Binding RenameSelected}" />
<KeyBinding Gesture="Delete" Command="{Binding DeleteSelected}" />
<KeyBinding Gesture="Ctrl+D" Command="{Binding DuplicateSelected}" />
<KeyBinding Gesture="Ctrl+C" Command="{Binding CopySelected}" />
<KeyBinding Gesture="Ctrl+V" Command="{Binding PasteSelected}" />
</UserControl.KeyBindings>
<UserControl.Resources> <UserControl.Resources>
<converters:ColorOpacityConverter x:Key="ColorOpacityConverter" /> <converters:ColorOpacityConverter x:Key="ColorOpacityConverter" />
</UserControl.Resources> </UserControl.Resources>
@ -55,7 +64,7 @@
<Setter Property="Background"> <Setter Property="Background">
<Setter.Value> <Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,28"> <LinearGradientBrush StartPoint="0,0" EndPoint="0,28">
<GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="0.0" /> <GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="0.0" />
<GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="0.05" /> <GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="0.05" />
<GradientStop Color="{Binding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0.25}" Offset="0.05" /> <GradientStop Color="{Binding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0.25}" Offset="0.05" />
<GradientStop Color="{Binding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0}" Offset="0.25" /> <GradientStop Color="{Binding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0}" Offset="0.25" />
@ -88,14 +97,6 @@
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" /> <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
</Style> </Style>
</TreeView.Styles> </TreeView.Styles>
<TreeView.KeyBindings>
<KeyBinding Gesture="Escape" Command="{CompiledBinding ClearSelection}" />
<KeyBinding Gesture="F2" Command="{CompiledBinding SelectedChild.Rename}" />
<KeyBinding Gesture="Delete" Command="{CompiledBinding SelectedChild.Delete}" />
<KeyBinding Gesture="Ctrl+D" Command="{CompiledBinding SelectedChild.Duplicate}" />
<KeyBinding Gesture="Ctrl+C" Command="{CompiledBinding SelectedChild.Copy}" />
<KeyBinding Gesture="Ctrl+V" Command="{CompiledBinding SelectedChild.Paste}" />
</TreeView.KeyBindings>
<TreeView.ItemTemplate> <TreeView.ItemTemplate>
<TreeDataTemplate ItemsSource="{Binding Children}"> <TreeDataTemplate ItemsSource="{Binding Children}">
<ContentControl Content="{Binding}" x:DataType="profileTree:TreeItemViewModel"> <ContentControl Content="{Binding}" x:DataType="profileTree:TreeItemViewModel">

View File

@ -2,7 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Reactive;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
@ -11,87 +10,114 @@ using Artemis.UI.Shared.Services.Interfaces;
using Artemis.UI.Shared.Services.ProfileEditor; using Artemis.UI.Shared.Services.ProfileEditor;
using ReactiveUI; 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; _profileEditorService = profileEditorService;
this.WhenActivated(d =>
public ProfileTreeViewModel(IWindowService windowService,
IProfileEditorService profileEditorService,
ILayerBrushService layerBrushService,
IProfileEditorVmFactory profileEditorVmFactory,
IRgbService rgbService)
: base(null, null, windowService, profileEditorService, rgbService, layerBrushService, profileEditorVmFactory)
{ {
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(); ProfileElement = configuration.Profile.GetRootFolder();
SubscribeToProfileElement(d); SubscribeToProfileElement(d);
CreateTreeItems(); CreateTreeItems();
}).DisposeWith(d); }).DisposeWith(d);
profileEditorService.ProfileElement.Subscribe(SelectCurrentProfileElement).DisposeWith(d); profileEditorService.ProfileElement.Subscribe(SelectCurrentProfileElement).DisposeWith(d);
}); });
this.WhenAnyValue(vm => vm.SelectedChild).Subscribe(model => this.WhenAnyValue(vm => vm.SelectedChild).Subscribe(model =>
{
if (model?.ProfileElement is RenderProfileElement renderProfileElement)
profileEditorService.ChangeCurrentProfileElement(renderProfileElement);
});
ClearSelection = ReactiveCommand.Create(() => profileEditorService.ChangeCurrentProfileElement(null));
}
public ReactiveCommand<Unit, Unit> ClearSelection { get; }
public TreeItemViewModel? SelectedChild
{ {
get => _selectedChild; if (model?.ProfileElement is RenderProfileElement renderProfileElement)
set => RaiseAndSetIfChanged(ref _selectedChild, value); 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<TreeItemViewModel> 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) currentParent.IsExpanded = true;
return; currentParent = currentParent.Parent;
// Find the tree item belonging to the selected element
List<TreeItemViewModel> 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;
} }
private List<TreeItemViewModel> GetAllTreeItems(ObservableCollection<TreeItemViewModel> treeItems) SelectedChild = selected;
}
private List<TreeItemViewModel> GetAllTreeItems(ObservableCollection<TreeItemViewModel> treeItems)
{
List<TreeItemViewModel> result = new();
foreach (TreeItemViewModel treeItemViewModel in treeItems)
{ {
List<TreeItemViewModel> result = new(); result.Add(treeItemViewModel);
foreach (TreeItemViewModel treeItemViewModel in treeItems) if (treeItemViewModel.Children.Any())
{ result.AddRange(GetAllTreeItems(treeItemViewModel.Children));
result.Add(treeItemViewModel);
if (treeItemViewModel.Children.Any())
result.AddRange(GetAllTreeItems(treeItemViewModel.Children));
}
return result;
} }
public override bool SupportsChildren => true; return result;
} }
} }

View File

@ -57,7 +57,14 @@ public class PropertiesViewModel : ActivatableViewModelBase
{ {
_profileElement = profileEditorService.ProfileElement.ToProperty(this, vm => vm.ProfileElement).DisposeWith(d); _profileElement = profileEditorService.ProfileElement.ToProperty(this, vm => vm.ProfileElement).DisposeWith(d);
_pixelsPerSecond = profileEditorService.PixelsPerSecond.ToProperty(this, vm => vm.PixelsPerSecond).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()); this.WhenAnyValue(vm => vm.ProfileElement).Subscribe(_ => UpdateGroups());
} }

View File

@ -23,7 +23,7 @@ public class TimelineGroupViewModel : ActivatableViewModelBase
{ {
_pixelsPerSecond = p; _pixelsPerSecond = p;
UpdateKeyframePositions(); UpdateKeyframePositions();
}); }).DisposeWith(d);
this.WhenAnyValue(vm => vm.PropertyGroupViewModel.IsExpanded).Subscribe(_ => 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); PropertyGroupViewModel.WhenAnyValue(vm => vm.IsExpanded).Subscribe(_ => this.RaisePropertyChanged(nameof(Children))).DisposeWith(d);
}); });

View File

@ -24,7 +24,7 @@ public class StatusBarViewModel : ActivatableViewModelBase
{ {
_profileElement = profileEditorService.ProfileElement.ToProperty(this, vm => vm.ProfileElement).DisposeWith(d); _profileElement = profileEditorService.ProfileElement.ToProperty(this, vm => vm.ProfileElement).DisposeWith(d);
_history = profileEditorService.History.ToProperty(this, vm => vm.History).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) this.WhenAnyValue(vm => vm.History)

View File

@ -9,6 +9,18 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.VisualEditor.VisualEditorView" x:Class="Artemis.UI.Screens.ProfileEditor.VisualEditor.VisualEditorView"
x:DataType="visualEditor:VisualEditorViewModel"> x:DataType="visualEditor:VisualEditorViewModel">
<UserControl.Resources>
<VisualBrush x:Key="LargeCheckerboardBrush" TileMode="Tile" Stretch="Uniform" SourceRect="0,0,20,20">
<VisualBrush.Visual>
<Canvas Width="20" Height="20">
<Rectangle Width="10" Height="10" Fill="Black" Opacity="0.15" />
<Rectangle Width="10" Height="10" Canvas.Left="10" />
<Rectangle Width="10" Height="10" Canvas.Top="10" />
<Rectangle Width="10" Height="10" Canvas.Left="10" Canvas.Top="10" Fill="Black" Opacity="0.15" />
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
</UserControl.Resources>
<Grid> <Grid>
<paz:ZoomBorder Name="ZoomBorder" <paz:ZoomBorder Name="ZoomBorder"
Stretch="None" Stretch="None"
@ -16,7 +28,7 @@
Focusable="True" Focusable="True"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Background="{DynamicResource LargeCheckerboardBrush}" Background="{StaticResource LargeCheckerboardBrush}"
ZoomChanged="ZoomBorder_OnZoomChanged"> ZoomChanged="ZoomBorder_OnZoomChanged">
<Grid Name="ContainerGrid" <Grid Name="ContainerGrid"
Background="Transparent"> Background="Transparent">

View File

@ -19,7 +19,6 @@ namespace Artemis.UI.Screens.ProfileEditor
private void MenuItem_OnSubmenuOpened(object? sender, RoutedEventArgs e) private void MenuItem_OnSubmenuOpened(object? sender, RoutedEventArgs e)
{ {
} }
} }
} }

View File

@ -15,6 +15,7 @@ using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Threading; using Avalonia.Threading;
using JetBrains.Annotations;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI.Screens.Root namespace Artemis.UI.Screens.Root
@ -95,6 +96,7 @@ namespace Artemis.UI.Screens.Root
{ {
_lifeTime.MainWindow = null; _lifeTime.MainWindow = null;
SidebarViewModel = null; SidebarViewModel = null;
Router.NavigateAndReset.Execute(new EmptyViewModel(this, "blank")).Subscribe();
OnMainWindowClosed(); OnMainWindowClosed();
} }
@ -220,4 +222,12 @@ namespace Artemis.UI.Screens.Root
#endregion #endregion
} }
internal class EmptyViewModel : MainScreenViewModel
{
/// <inheritdoc />
public EmptyViewModel(IScreen hostScreen, string urlPathSegment) : base(hostScreen, urlPathSegment)
{
}
}
} }

View File

@ -4,6 +4,7 @@ using System.Collections.ObjectModel;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Reactive; using System.Reactive;
using System.Reactive.Disposables;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
@ -30,7 +31,7 @@ namespace Artemis.UI.Screens.Settings
DisplayName = "General"; DisplayName = "General";
_settingsService = settingsService; _settingsService = settingsService;
_debugService = debugService; _debugService = debugService;
_fluentAvaloniaTheme = AvaloniaLocator.Current.GetService<FluentAvaloniaTheme>(); ; _fluentAvaloniaTheme = AvaloniaLocator.Current.GetService<FluentAvaloniaTheme>();
List<LayerBrushProvider> layerBrushProviders = pluginManagementService.GetFeaturesOfType<LayerBrushProvider>(); List<LayerBrushProvider> layerBrushProviders = pluginManagementService.GetFeaturesOfType<LayerBrushProvider>();
LayerBrushDescriptors = new ObservableCollection<LayerBrushDescriptor>(layerBrushProviders.SelectMany(l => l.LayerBrushDescriptors)); LayerBrushDescriptors = new ObservableCollection<LayerBrushDescriptor>(layerBrushProviders.SelectMany(l => l.LayerBrushDescriptors));
@ -46,7 +47,11 @@ namespace Artemis.UI.Screens.Settings
ShowDebugger = ReactiveCommand.Create(ExecuteShowDebugger); ShowDebugger = ReactiveCommand.Create(ExecuteShowDebugger);
ShowDataFolder = ReactiveCommand.Create(ExecuteShowDataFolder); 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) private void UIColorSchemeOnSettingChanged(object? sender, EventArgs e)

View File

@ -7,7 +7,18 @@
xmlns:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared" xmlns:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.SurfaceEditor.SurfaceEditorView"> x:Class="Artemis.UI.Screens.SurfaceEditor.SurfaceEditorView">
<UserControl.Resources>
<VisualBrush x:Key="LargeCheckerboardBrush" TileMode="Tile" Stretch="Uniform" SourceRect="0,0,20,20">
<VisualBrush.Visual>
<Canvas Width="20" Height="20">
<Rectangle Width="10" Height="10" Fill="Black" Opacity="0.15" />
<Rectangle Width="10" Height="10" Canvas.Left="10" />
<Rectangle Width="10" Height="10" Canvas.Top="10" />
<Rectangle Width="10" Height="10" Canvas.Left="10" Canvas.Top="10" Fill="Black" Opacity="0.15" />
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
</UserControl.Resources>
<Border Classes="router-container"> <Border Classes="router-container">
<paz:ZoomBorder Name="ZoomBorder" <paz:ZoomBorder Name="ZoomBorder"
Stretch="None" Stretch="None"
@ -15,7 +26,7 @@
Focusable="True" Focusable="True"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Background="{DynamicResource LargeCheckerboardBrush}" Background="{StaticResource LargeCheckerboardBrush}"
ZoomChanged="ZoomBorder_OnZoomChanged" ZoomChanged="ZoomBorder_OnZoomChanged"
PointerPressed="ZoomBorder_OnPointerPressed" PointerPressed="ZoomBorder_OnPointerPressed"
PointerMoved="ZoomBorder_OnPointerMoved" PointerMoved="ZoomBorder_OnPointerMoved"

View File

@ -47,7 +47,7 @@
Padding="4" Padding="4"
Canvas.Left="{CompiledBinding ValuePoint.X}" Canvas.Left="{CompiledBinding ValuePoint.X}"
Canvas.Top="{CompiledBinding ValuePoint.Y}" Canvas.Top="{CompiledBinding ValuePoint.Y}"
IsVisible="{CompiledBinding FromViewModel.Pin.PinValue, Converter={x:Static ObjectConverters.IsNotNull}}"> IsVisible="{CompiledBinding DisplayValue}">
<ContentControl Content="{CompiledBinding FromViewModel.Pin.PinValue}"> <ContentControl Content="{CompiledBinding FromViewModel.Pin.PinValue}">
<ContentControl.DataTemplates> <ContentControl.DataTemplates>
<DataTemplate DataType="skiaSharp:SKColor"> <DataTemplate DataType="skiaSharp:SKColor">

View File

@ -15,14 +15,15 @@ namespace Artemis.UI.Screens.VisualScripting;
public class CableViewModel : ActivatableViewModelBase public class CableViewModel : ActivatableViewModelBase
{ {
private readonly ObservableAsPropertyHelper<bool> _connected;
private readonly ObservableAsPropertyHelper<Point> _fromPoint;
private readonly ObservableAsPropertyHelper<Point> _toPoint;
private readonly ObservableAsPropertyHelper<Point> _valuePoint;
private ObservableAsPropertyHelper<Color>? _cableColor; private ObservableAsPropertyHelper<Color>? _cableColor;
private ObservableAsPropertyHelper<Point>? _fromPoint;
private ObservableAsPropertyHelper<Point>? _toPoint;
private ObservableAsPropertyHelper<Point>? _valuePoint;
private ObservableAsPropertyHelper<bool>? _connected;
private PinViewModel? _fromViewModel; private PinViewModel? _fromViewModel;
private PinViewModel? _toViewModel; private PinViewModel? _toViewModel;
private bool _displayValue;
public CableViewModel(NodeScriptViewModel nodeScriptViewModel, IPin from, IPin to) public CableViewModel(NodeScriptViewModel nodeScriptViewModel, IPin from, IPin to)
{ {
@ -42,25 +43,32 @@ public class CableViewModel : ActivatableViewModelBase
.Switch() .Switch()
.ToProperty(this, vm => vm.CableColor) .ToProperty(this, vm => vm.CableColor)
.DisposeWith(d); .DisposeWith(d);
_fromPoint = this.WhenAnyValue(vm => vm.FromViewModel)
.Select(p => p != null ? p.WhenAnyValue(pvm => pvm.Position) : Observable.Never<Point>())
.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<Point>())
.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<Point>())
.Switch()
.ToProperty(this, vm => vm.FromPoint);
_toPoint = this.WhenAnyValue(vm => vm.ToViewModel)
.Select(p => p != null ? p.WhenAnyValue(pvm => pvm.Position) : Observable.Never<Point>())
.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) DisplayValue = !nodeScriptViewModel.IsPreview;
_connected = this.WhenAnyValue(vm => vm.FromPoint, vm => vm.ToPoint)
.Select(tuple => tuple.Item1 != new Point(0, 0) && tuple.Item2 != new Point(0, 0))
.ToProperty(this, vm => vm.Connected);
} }
public PinViewModel? FromViewModel public PinViewModel? FromViewModel
@ -75,10 +83,16 @@ public class CableViewModel : ActivatableViewModelBase
set => RaiseAndSetIfChanged(ref _toViewModel, value); set => RaiseAndSetIfChanged(ref _toViewModel, value);
} }
public bool Connected => _connected.Value; public bool DisplayValue
{
get => _displayValue;
set => _displayValue = value;
}
public Point FromPoint => _fromPoint.Value; public bool Connected => _connected?.Value ?? false;
public Point ToPoint => _toPoint.Value;
public Point ValuePoint => _valuePoint.Value; 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); public Color CableColor => _cableColor?.Value ?? new Color(255, 255, 255, 255);
} }

View File

@ -21,7 +21,7 @@
FontFamily="{StaticResource SymbolThemeFontFamily}" FontFamily="{StaticResource SymbolThemeFontFamily}"
Classes="AppBarButton" Classes="AppBarButton"
Command="{Binding $parent[TextBox].Clear}" 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}}" />
<controls:Button Content="&#xE721;" <controls:Button Content="&#xE721;"
FontFamily="{StaticResource SymbolThemeFontFamily}" FontFamily="{StaticResource SymbolThemeFontFamily}"
Classes="AppBarButton" Classes="AppBarButton"

View File

@ -14,63 +14,64 @@
<Setter Property="MaxWidth" Value="1000"></Setter> <Setter Property="MaxWidth" Value="1000"></Setter>
</Style> </Style>
</UserControl.Styles> </UserControl.Styles>
<UserControl.KeyBindings> <UserControl.Resources>
<KeyBinding Command="{CompiledBinding History.Undo}" Gesture="Ctrl+Z"></KeyBinding> <VisualBrush x:Key="LargeCheckerboardBrush" TileMode="Tile" Stretch="Uniform" SourceRect="0,0,20,20">
<KeyBinding Command="{CompiledBinding History.Redo}" Gesture="Ctrl+Y"></KeyBinding> <VisualBrush.Visual>
<KeyBinding Command="{Binding DeleteSelectedNodes}" Gesture="Delete"></KeyBinding> <Canvas Width="20" Height="20">
</UserControl.KeyBindings> <Rectangle Width="10" Height="10" Fill="Black" Opacity="0.15" />
<Rectangle Width="10" Height="10" Canvas.Left="10" />
<Rectangle Width="10" Height="10" Canvas.Top="10" />
<Rectangle Width="10" Height="10" Canvas.Left="10" Canvas.Top="10" Fill="Black" Opacity="0.15" />
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
</UserControl.Resources>
<paz:ZoomBorder Name="ZoomBorder" <paz:ZoomBorder Name="ZoomBorder"
Stretch="None" Stretch="None"
Focusable="True" Focusable="True"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Background="{DynamicResource LargeCheckerboardBrush}" Background="{StaticResource LargeCheckerboardBrush}"
ZoomChanged="ZoomBorder_OnZoomChanged" ZoomChanged="ZoomBorder_OnZoomChanged"
MaxZoomX="1" MaxZoomX="1"
MaxZoomY="1" MaxZoomY="1"
EnableConstrains="True" EnableConstrains="True"
PointerReleased="ZoomBorder_OnPointerReleased"> PointerReleased="ZoomBorder_OnPointerReleased">
<Grid Name="ContainerGrid" Background="Transparent" ClipToBounds="False"> <Grid Name="ContainerGrid" Background="Transparent" ClipToBounds="False">
<Grid.ContextFlyout> <Grid.ContextFlyout>
<Flyout FlyoutPresenterClasses="node-picker-flyout"> <Flyout FlyoutPresenterClasses="node-picker-flyout">
<ContentControl Content="{CompiledBinding NodePickerViewModel}" /> <ContentControl Content="{CompiledBinding NodePickerViewModel}" />
</Flyout> </Flyout>
</Grid.ContextFlyout> </Grid.ContextFlyout>
<Grid.Transitions> <Grid.Transitions>
<Transitions> <Transitions>
<TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.2" Easing="CubicEaseOut" /> <TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.2" Easing="CubicEaseOut" />
</Transitions> </Transitions>
</Grid.Transitions> </Grid.Transitions>
<!-- Drag cable, if any --> <!-- Drag cable, if any -->
<ContentControl Content="{CompiledBinding DragViewModel}" ClipToBounds="False"/> <ContentControl Content="{CompiledBinding DragViewModel}" ClipToBounds="False" />
<!-- Cables --> <!-- Cables -->
<ItemsControl Items="{CompiledBinding CableViewModels}" ClipToBounds="False"> <ItemsControl Items="{CompiledBinding CableViewModels}" ClipToBounds="False">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<Canvas /> <Canvas />
</ItemsPanelTemplate> </ItemsPanelTemplate>
</ItemsControl.ItemsPanel> </ItemsControl.ItemsPanel>
<ItemsControl.Styles>
<Style Selector="ItemsControl > ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Node.X, TargetNullValue=0}" />
<Setter Property="Canvas.Top" Value="{Binding Node.Y, TargetNullValue=0}" />
</Style>
</ItemsControl.Styles>
</ItemsControl> </ItemsControl>
<!-- Nodes --> <!-- Nodes -->
<ItemsControl Name="NodesContainer" Items="{CompiledBinding NodeViewModels}" ClipToBounds="False"> <ItemsControl Name="NodesContainer" Items="{CompiledBinding NodeViewModels}" ClipToBounds="False">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<Canvas /> <Canvas />
</ItemsPanelTemplate> </ItemsPanelTemplate>
</ItemsControl.ItemsPanel> </ItemsControl.ItemsPanel>
<ItemsControl.Styles> <ItemsControl.Styles>
<Style Selector="ItemsControl > ContentPresenter"> <Style Selector="ItemsControl#NodesContainer > ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Node.X, TargetNullValue=0}" /> <Setter Property="Canvas.Left" Value="{Binding Node.X}" />
<Setter Property="Canvas.Top" Value="{Binding Node.Y, TargetNullValue=0}" /> <Setter Property="Canvas.Top" Value="{Binding Node.Y}" />
</Style> </Style>
</ItemsControl.Styles> </ItemsControl.Styles>
</ItemsControl> </ItemsControl>

View File

@ -1,7 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq; using System.Linq;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Events; using Artemis.Core.Events;
using Artemis.UI.Shared.Controls; using Artemis.UI.Shared.Controls;
@ -15,6 +17,8 @@ using Avalonia.Interactivity;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Avalonia.Threading;
using DynamicData.Binding;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI.Screens.VisualScripting; namespace Artemis.UI.Screens.VisualScripting;
@ -38,27 +42,17 @@ public class NodeScriptView : ReactiveUserControl<NodeScriptViewModel>
UpdateZoomBorderBackground(); UpdateZoomBorderBackground();
_grid.AddHandler(PointerReleasedEvent, CanvasOnPointerReleased, RoutingStrategies.Direct | RoutingStrategies.Tunnel | RoutingStrategies.Bubble, true); _grid.AddHandler(PointerReleasedEvent, CanvasOnPointerReleased, RoutingStrategies.Direct | RoutingStrategies.Tunnel | RoutingStrategies.Bubble, true);
this.WhenActivated(d => this.WhenActivated(d =>
{ {
ViewModel!.PickerPositionSubject.Subscribe(p => ViewModel!.PickerPositionSubject.Subscribe(ShowPickerAt).DisposeWith(d);
{
ViewModel.NodePickerViewModel.Position = p;
_grid?.ContextFlyout?.ShowAt(_grid, true);
}).DisposeWith(d);
if (ViewModel.IsPreview) if (ViewModel.IsPreview)
{ {
BoundsProperty.Changed.Subscribe(BoundsPropertyChanged).DisposeWith(d); BoundsProperty.Changed.Subscribe(BoundsPropertyChanged).DisposeWith(d);
ViewModel.NodeScript.NodeAdded += NodesChanged; ViewModel.NodeViewModels.ToObservableChangeSet().Subscribe(_ => AutoFitIfPreview()).DisposeWith(d);
ViewModel.NodeScript.NodeRemoved += NodesChanged;
Disposable.Create(() =>
{
ViewModel.NodeScript.NodeAdded -= NodesChanged;
ViewModel.NodeScript.NodeRemoved -= NodesChanged;
}).DisposeWith(d);
} }
AutoFit(true); Dispatcher.UIThread.InvokeAsync(() => AutoFit(true), DispatcherPriority.ContextIdle);
}); });
} }
@ -68,17 +62,20 @@ public class NodeScriptView : ReactiveUserControl<NodeScriptViewModel>
return base.MeasureOverride(availableSize); 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() private void AutoFitIfPreview()
{ {
if (ViewModel != null && ViewModel.IsPreview) if (ViewModel != null && ViewModel.IsPreview)
AutoFit(true); AutoFit(true);
} }
private void NodesChanged(object? sender, SingleValueEventArgs<INode> e)
{
AutoFitIfPreview();
}
private void BoundsPropertyChanged(AvaloniaPropertyChangedEventArgs<Rect> obj) private void BoundsPropertyChanged(AvaloniaPropertyChangedEventArgs<Rect> obj)
{ {
if (_nodesContainer.ItemContainerGenerator.Containers.Select(c => c.ContainerControl).Contains(obj.Sender)) if (_nodesContainer.ItemContainerGenerator.Containers.Select(c => c.ContainerControl).Contains(obj.Sender))
@ -102,8 +99,8 @@ public class NodeScriptView : ReactiveUserControl<NodeScriptViewModel>
double bottom = _nodesContainer.ItemContainerGenerator.Containers.Select(c => c.ContainerControl.Bounds.Bottom).Max(); double bottom = _nodesContainer.ItemContainerGenerator.Containers.Select(c => c.ContainerControl.Bounds.Bottom).Max();
double right = _nodesContainer.ItemContainerGenerator.Containers.Select(c => c.ContainerControl.Bounds.Right).Max(); double right = _nodesContainer.ItemContainerGenerator.Containers.Select(c => c.ContainerControl.Bounds.Right).Max();
// Add a 5 pixel margin around the rect // Add a 10 pixel margin around the rect
Rect scriptRect = new(new Point(left - 5, top - 5), new Point(right + 5, bottom + 5)); Rect scriptRect = new(new Point(left - 10, top - 10), new Point(right + 10, bottom + 10));
// The scale depends on the available space // The scale depends on the available space
double scale = Math.Min(1, Math.Min(Bounds.Width / scriptRect.Width, Bounds.Height / scriptRect.Height)); double scale = Math.Min(1, Math.Min(Bounds.Width / scriptRect.Width, Bounds.Height / scriptRect.Height));

View File

@ -63,7 +63,10 @@
<ItemsControl Items="{CompiledBinding InputPinCollectionViewModels}" /> <ItemsControl Items="{CompiledBinding InputPinCollectionViewModels}" />
</StackPanel> </StackPanel>
<ContentControl Grid.Column="1" Name="CustomViewModelContainer" Content="{CompiledBinding CustomNodeViewModel}" IsVisible="{CompiledBinding CustomNodeViewModel}" /> <ContentControl Grid.Column="1"
Name="CustomViewModelContainer"
Content="{CompiledBinding CustomNodeViewModel}"
IsVisible="{CompiledBinding CustomNodeViewModel, Converter={x:Static ObjectConverters.IsNotNull}}" />
<StackPanel Grid.Column="2" IsVisible="{CompiledBinding HasOutputPins}"> <StackPanel Grid.Column="2" IsVisible="{CompiledBinding HasOutputPins}">
<ItemsControl Items="{CompiledBinding OutputPinViewModels}" Margin="4 0" /> <ItemsControl Items="{CompiledBinding OutputPinViewModels}" Margin="4 0" />

View File

@ -2,7 +2,10 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Mixins;
using Avalonia.Controls.Presenters;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Avalonia.VisualTree; using Avalonia.VisualTree;

View File

@ -35,8 +35,8 @@ public class NodeViewModel : ActivatableViewModelBase
public NodeViewModel(NodeScriptViewModel nodeScriptViewModel, INode node, INodeVmFactory nodeVmFactory, INodeEditorService nodeEditorService) public NodeViewModel(NodeScriptViewModel nodeScriptViewModel, INode node, INodeVmFactory nodeVmFactory, INodeEditorService nodeEditorService)
{ {
NodeScriptViewModel = nodeScriptViewModel;
_nodeEditorService = nodeEditorService; _nodeEditorService = nodeEditorService;
NodeScriptViewModel = nodeScriptViewModel;
Node = node; Node = node;
DeleteNode = ReactiveCommand.Create(ExecuteDeleteNode, this.WhenAnyValue(vm => vm.IsStaticNode).Select(v => !v)); DeleteNode = ReactiveCommand.Create(ExecuteDeleteNode, this.WhenAnyValue(vm => vm.IsStaticNode).Select(v => !v));

View File

@ -3,28 +3,22 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:pins="clr-namespace:Artemis.UI.Screens.VisualScripting.Pins" xmlns:pins="clr-namespace:Artemis.UI.Screens.VisualScripting.Pins"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignWidth="200" d:DesignWidth="200"
x:Class="Artemis.UI.Screens.VisualScripting.Pins.InputPinView" x:Class="Artemis.UI.Screens.VisualScripting.Pins.InputPinView"
x:DataType="pins:PinViewModel"> x:DataType="pins:PinViewModel">
<UserControl.Resources>
<converters:ColorToSolidColorBrushConverter x:Key="ColorToSolidColorBrushConverter" />
</UserControl.Resources>
<UserControl.Styles> <UserControl.Styles>
<StyleInclude Source="/Screens/VisualScripting/VisualScripting.axaml" /> <StyleInclude Source="/Screens/VisualScripting/VisualScripting.axaml" />
<Style Selector="StackPanel#PinContainer Border#VisualPinPoint">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="{CompiledBinding DarkenedPinColor}"></SolidColorBrush>
</Setter.Value>
</Setter>
<Setter Property="BorderBrush">
<Setter.Value>
<SolidColorBrush Color="{CompiledBinding PinColor}"></SolidColorBrush>
</Setter.Value>
</Setter>
</Style>
</UserControl.Styles> </UserControl.Styles>
<StackPanel Name="PinContainer" Orientation="Horizontal" Spacing="6"> <StackPanel Name="PinContainer" Orientation="Horizontal" Spacing="6">
<Border Name="PinPoint"> <Border Name="PinPoint">
<Border Name="VisualPinPoint" /> <Border Name="VisualPinPoint"
BorderBrush="{CompiledBinding PinColor, Converter={StaticResource ColorToSolidColorBrushConverter}}"
Background="{CompiledBinding DarkenedPinColor, Converter={StaticResource ColorToSolidColorBrushConverter}}"/>
</Border> </Border>
<TextBlock Name="PinName" VerticalAlignment="Center" Text="{CompiledBinding Pin.Name}" /> <TextBlock Name="PinName" VerticalAlignment="Center" Text="{CompiledBinding Pin.Name}" />
</StackPanel> </StackPanel>

View File

@ -3,29 +3,23 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:pins="clr-namespace:Artemis.UI.Screens.VisualScripting.Pins" xmlns:pins="clr-namespace:Artemis.UI.Screens.VisualScripting.Pins"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignWidth="200" d:DesignWidth="200"
x:Class="Artemis.UI.Screens.VisualScripting.Pins.OutputPinView" x:Class="Artemis.UI.Screens.VisualScripting.Pins.OutputPinView"
x:DataType="pins:PinViewModel"> x:DataType="pins:PinViewModel">
<UserControl.Resources>
<converters:ColorToSolidColorBrushConverter x:Key="ColorToSolidColorBrushConverter" />
</UserControl.Resources>
<UserControl.Styles> <UserControl.Styles>
<StyleInclude Source="/Screens/VisualScripting/VisualScripting.axaml" /> <StyleInclude Source="/Screens/VisualScripting/VisualScripting.axaml" />
<Style Selector="StackPanel#PinContainer Border#VisualPinPoint">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="{CompiledBinding DarkenedPinColor}" />
</Setter.Value>
</Setter>
<Setter Property="BorderBrush">
<Setter.Value>
<SolidColorBrush Color="{CompiledBinding PinColor}" />
</Setter.Value>
</Setter>
</Style>
</UserControl.Styles> </UserControl.Styles>
<StackPanel Name="PinContainer" Orientation="Horizontal" Spacing="6"> <StackPanel Name="PinContainer" Orientation="Horizontal" Spacing="6">
<TextBlock Name="PinName" VerticalAlignment="Center" Text="{CompiledBinding Pin.Name}" /> <TextBlock Name="PinName" VerticalAlignment="Center" Text="{CompiledBinding Pin.Name}" />
<Border Name="PinPoint"> <Border Name="PinPoint">
<Border Name="VisualPinPoint" /> <Border Name="VisualPinPoint"
BorderBrush="{CompiledBinding PinColor, Converter={StaticResource ColorToSolidColorBrushConverter}}"
Background="{CompiledBinding DarkenedPinColor, Converter={StaticResource ColorToSolidColorBrushConverter}}"/>
</Border> </Border>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@ -23,7 +23,11 @@ public class ViewLocator : IDataTemplate
throw new ArtemisUIException($"The views of activatable view models should inherit ReactiveUserControl<T>, in this case ReactiveUserControl<{data.GetType().Name}>."); throw new ArtemisUIException($"The views of activatable view models should inherit ReactiveUserControl<T>, in this case ReactiveUserControl<{data.GetType().Name}>.");
if (type != null) if (type != null)
{
Debug.WriteLine("[ViewLocator] Creating instance of '{0}'", type);
return (Control) Activator.CreateInstance(type)!; return (Control) Activator.CreateInstance(type)!;
}
return new TextBlock {Text = "Not Found: " + name}; return new TextBlock {Text = "Not Found: " + name};
} }