Core - Refactored effects

Profile editor - Added effect creation
This commit is contained in:
Robert committed 2022-04-23 17:07:04 +02:00
1 parent e5ba48c7f4
commit 2bf36fbf20
61 files changed
+1005 -763

No files matched your search

@@ -77,7 +77,7 @@ namespace Artemis.UI.Screens.Device
await _windowService.CreateContentDialog()
.WithTitle($"{Device.RgbDevice.DeviceInfo.DeviceName} - Detect input")
.WithViewModel<DeviceDetectInputViewModel>(out var viewModel, ("device", Device))
.WithViewModel<DeviceDetectInputViewModel>(out DeviceDetectInputViewModel? viewModel, ("device", Device))
.WithCloseButtonText("Cancel")
.ShowAsync();
@@ -0,0 +1,64 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:layerEffects="clr-namespace:Artemis.Core.LayerEffects;assembly=Artemis.Core"
xmlns:dialogs="clr-namespace:Artemis.UI.Screens.ProfileEditor.Properties.Dialogs"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.Properties.Dialogs.AddEffectView"
x:DataType="dialogs:AddEffectViewModel"
Width="500">
<Grid RowDefinitions="Auto,*">
<TextBox Name="SearchBox" Text="{CompiledBinding SearchText}" Margin="0 0 0 15" Watermark="Search"></TextBox>
<ListBox Name="EffectDescriptorsList"
Grid.Row="1"
Items="{CompiledBinding LayerEffectDescriptors}"
IsVisible="{CompiledBinding LayerEffectDescriptors.Count}"
Height="300">
<ListBox.DataTemplates>
<DataTemplate DataType="{x:Type layerEffects:LayerEffectDescriptor}">
<Grid RowDefinitions="Auto,*"
ColumnDefinitions="Auto,Auto"
Background="Transparent"
PointerReleased="InputElement_OnPointerReleased"
Margin="0 4"
VerticalAlignment="Center">
<shared:ArtemisIcon Grid.Column="0"
Grid.RowSpan="2"
Icon="{CompiledBinding Icon}"
Width="24"
Height="24"
VerticalAlignment="Center"
Margin="0 0 15 0" />
<TextBlock Grid.Column="1"
Grid.Row="0"
Classes="BodyStrongTextBlockStyle"
Text="{CompiledBinding DisplayName}"
VerticalAlignment="Bottom"
Width="450"
TextWrapping="Wrap" />
<TextBlock Grid.Column="1"
Grid.Row="1"
Foreground="{DynamicResource TextFillColorSecondary}"
Text="{CompiledBinding Description}"
VerticalAlignment="Top"
Width="450"
TextWrapping="Wrap" />
</Grid>
</DataTemplate>
</ListBox.DataTemplates>
</ListBox>
<Grid Grid.Row="1" Height="300">
<StackPanel VerticalAlignment="Center"
Spacing="20"
IsVisible="{CompiledBinding !LayerEffectDescriptors.Count}">
<avalonia:MaterialIcon Kind="CloseCircle" Width="32" Height="32"></avalonia:MaterialIcon>
<TextBlock Classes="h5" TextAlignment="Center">None of the effects match your search</TextBlock>
</StackPanel>
</Grid>
</Grid>
</UserControl>
@@ -0,0 +1,28 @@
using Artemis.Core.LayerEffects;
using Avalonia;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.ProfileEditor.Properties.Dialogs;
public class AddEffectView : ReactiveUserControl<AddEffectViewModel>
{
public AddEffectView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void InputElement_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
{
if (sender is not IDataContextProvider {DataContext: LayerEffectDescriptor descriptor} || ViewModel == null)
return;
ViewModel?.AddLayerEffect(descriptor);
}
}
@@ -0,0 +1,62 @@
using System;
using System.Collections.ObjectModel;
using System.Reactive.Linq;
using Artemis.Core;
using Artemis.Core.LayerEffects;
using Artemis.Core.Services;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
using DynamicData;
using ReactiveUI;
namespace Artemis.UI.Screens.ProfileEditor.Properties.Dialogs;
public class AddEffectViewModel : ContentDialogViewModelBase
{
private readonly RenderProfileElement _renderProfileElement;
private readonly IProfileEditorService _profileEditorService;
private string? _searchText;
public AddEffectViewModel(RenderProfileElement renderProfileElement, IProfileEditorService profileEditorService, ILayerEffectService layerEffectService)
{
_renderProfileElement = renderProfileElement;
_profileEditorService = profileEditorService;
SourceList<LayerEffectDescriptor> layerEffectSourceList = new();
layerEffectSourceList.AddRange(layerEffectService.GetLayerEffects());
IObservable<Func<LayerEffectDescriptor, bool>> layerEffectFilter = this.WhenAnyValue(vm => vm.SearchText).Select(CreatePredicate);
layerEffectSourceList.Connect()
.Filter(layerEffectFilter)
.Bind(out ReadOnlyObservableCollection<LayerEffectDescriptor> layerEffectDescriptors)
.Subscribe();
LayerEffectDescriptors = layerEffectDescriptors;
}
public ReadOnlyObservableCollection<LayerEffectDescriptor> LayerEffectDescriptors { get; }
public string? SearchText
{
get => _searchText;
set => this.RaiseAndSetIfChanged(ref _searchText, value);
}
public void AddLayerEffect(LayerEffectDescriptor descriptor)
{
BaseLayerEffect layerEffect = descriptor.CreateInstance(_renderProfileElement, null);
_profileEditorService.ExecuteCommand(new AddLayerEffect(_renderProfileElement, layerEffect));
ContentDialog?.Hide();
}
private Func<LayerEffectDescriptor, bool> CreatePredicate(string? search)
{
if (string.IsNullOrWhiteSpace(search))
return _ => true;
search = search.Trim();
return data => data.DisplayName.Contains(search, StringComparison.InvariantCultureIgnoreCase) ||
data.Description.Contains(search, StringComparison.InvariantCultureIgnoreCase);
}
}
@@ -13,7 +13,7 @@
<StyleInclude Source="/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/Segment.axaml" />
</UserControl.Styles>
<UserControl.Resources>
<converters:DoubleToGridLengthConverter x:Key="DoubleToGridLengthConverter"></converters:DoubleToGridLengthConverter>
<converters:DoubleToGridLengthConverter x:Key="DoubleToGridLengthConverter"></converters:DoubleToGridLengthConverter>
</UserControl.Resources>
<Grid Name="ContainerGrid">
<Grid.ColumnDefinitions>
@@ -29,13 +29,22 @@
Name="TreeScrollViewer"
Offset="{CompiledBinding #TimelineScrollViewer.Offset, Mode=OneWay}"
Background="{DynamicResource CardStrokeColorDefaultSolidBrush}">
<ItemsControl Items="{CompiledBinding PropertyGroupViewModels}" Padding="0 0 8 0">
<ItemsControl.ItemTemplate>
<TreeDataTemplate DataType="{x:Type local:PropertyGroupViewModel}" ItemsSource="{CompiledBinding Children}">
<ContentControl Content="{CompiledBinding TreeGroupViewModel}" />
</TreeDataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Grid RowDefinitions="*,Auto">
<ItemsControl Items="{CompiledBinding PropertyGroupViewModels}" Padding="0 0 8 0">
<ItemsControl.ItemTemplate>
<TreeDataTemplate DataType="{x:Type local:PropertyGroupViewModel}" ItemsSource="{CompiledBinding Children}">
<ContentControl Content="{CompiledBinding TreeGroupViewModel}" />
</TreeDataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Grid.Row="1"
Command="{CompiledBinding AddEffect}"
Margin="4"
VerticalAlignment="Bottom"
HorizontalAlignment="Right">
Add new effect
</Button>
</Grid>
</ScrollViewer>
</Grid>
@@ -45,12 +54,12 @@
Background="Transparent"
Margin="0 0 -5 0" />
<ContentControl Grid.Column="2"
<ContentControl Grid.Column="2"
IsVisible="{CompiledBinding DataBindingViewModel, Converter={x:Static ObjectConverters.IsNotNull}}"
Content="{CompiledBinding DataBindingViewModel}"/>
Content="{CompiledBinding DataBindingViewModel}" />
<!-- Horizontal scrolling -->
<ScrollViewer Grid.Column="2"
<!-- Horizontal scrolling -->
<ScrollViewer Grid.Column="2"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Disabled"
IsVisible="{CompiledBinding DataBindingViewModel, Converter={x:Static ObjectConverters.IsNull}}">
@@ -5,6 +5,7 @@ using System.Linq;
using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.LayerBrushes;
using Artemis.Core.LayerEffects;
@@ -12,8 +13,12 @@ using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.Playback;
using Artemis.UI.Screens.ProfileEditor.Properties.DataBinding;
using Artemis.UI.Screens.ProfileEditor.Properties.Dialogs;
using Artemis.UI.Screens.ProfileEditor.Properties.Timeline;
using Artemis.UI.Screens.Sidebar;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders;
using Artemis.UI.Shared.Services.ProfileEditor;
using ReactiveUI;
@@ -23,6 +28,8 @@ public class PropertiesViewModel : ActivatableViewModelBase
{
private readonly Dictionary<LayerPropertyGroup, PropertyGroupViewModel> _cachedPropertyViewModels;
private readonly IDataBindingVmFactory _dataBindingVmFactory;
private readonly IWindowService _windowService;
private readonly ILayerEffectService _layerEffectService;
private readonly ILayerPropertyVmFactory _layerPropertyVmFactory;
private readonly IProfileEditorService _profileEditorService;
private readonly ISettingsService _settingsService;
@@ -38,18 +45,22 @@ public class PropertiesViewModel : ActivatableViewModelBase
ISettingsService settingsService,
ILayerPropertyVmFactory layerPropertyVmFactory,
IDataBindingVmFactory dataBindingVmFactory,
IWindowService windowService,
ILayerEffectService layerEffectService,
PlaybackViewModel playbackViewModel)
{
_profileEditorService = profileEditorService;
_settingsService = settingsService;
_layerPropertyVmFactory = layerPropertyVmFactory;
_dataBindingVmFactory = dataBindingVmFactory;
_windowService = windowService;
_layerEffectService = layerEffectService;
_cachedPropertyViewModels = new Dictionary<LayerPropertyGroup, PropertyGroupViewModel>();
PropertyGroupViewModels = new ObservableCollection<PropertyGroupViewModel>();
PlaybackViewModel = playbackViewModel;
TimelineViewModel = layerPropertyVmFactory.TimelineViewModel(PropertyGroupViewModels);
AddEffect = ReactiveCommand.CreateFromTask(ExecuteAddEffect);
// React to service profile element changes as long as the VM is active
this.WhenActivated(d =>
{
@@ -86,6 +97,7 @@ public class PropertiesViewModel : ActivatableViewModelBase
public ObservableCollection<PropertyGroupViewModel> PropertyGroupViewModels { get; }
public PlaybackViewModel PlaybackViewModel { get; }
public TimelineViewModel TimelineViewModel { get; }
public ReactiveCommand<Unit, Unit> AddEffect { get; }
public DataBindingViewModel? DataBindingViewModel
{
@@ -102,6 +114,17 @@ public class PropertiesViewModel : ActivatableViewModelBase
public IObservable<bool> Playing => _profileEditorService.Playing;
public PluginSetting<double> PropertiesTreeWidth => _settingsService.GetSetting("ProfileEditor.PropertiesTreeWidth", 500.0);
private async Task ExecuteAddEffect()
{
if (ProfileElement == null)
return;
await _windowService.CreateContentDialog()
.WithTitle("Add layer effect")
.WithViewModel(out AddEffectViewModel _, ("renderProfileElement", ProfileElement))
.WithCloseButtonText("Cancel")
.ShowAsync();
}
private void UpdatePropertyGroups()
{
@@ -38,18 +38,36 @@ public class PropertyGroupViewModel : ViewModelBase, IDisposable
PopulateChildren();
}
public PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, ILayerPropertyVmFactory layerPropertyVmFactory, IPropertyInputService propertyInputService,
BaseLayerBrush layerBrush)
: this(layerPropertyGroup, layerPropertyVmFactory, propertyInputService)
public PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, ILayerPropertyVmFactory layerPropertyVmFactory, IPropertyInputService propertyInputService, BaseLayerBrush layerBrush)
{
_layerPropertyVmFactory = layerPropertyVmFactory;
_propertyInputService = propertyInputService;
LayerBrush = layerBrush;
Children = new ObservableCollection<ViewModelBase>();
LayerPropertyGroup = layerPropertyGroup;
TreeGroupViewModel = layerPropertyVmFactory.TreeGroupViewModel(this);
TimelineGroupViewModel = layerPropertyVmFactory.TimelineGroupViewModel(this);
LayerPropertyGroup.VisibilityChanged += LayerPropertyGroupOnVisibilityChanged;
_isVisible = !LayerPropertyGroup.IsHidden;
PopulateChildren();
}
public PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, ILayerPropertyVmFactory layerPropertyVmFactory, IPropertyInputService propertyInputService,
BaseLayerEffect layerEffect)
: this(layerPropertyGroup, layerPropertyVmFactory, propertyInputService)
public PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, ILayerPropertyVmFactory layerPropertyVmFactory, IPropertyInputService propertyInputService, BaseLayerEffect layerEffect)
{
_layerPropertyVmFactory = layerPropertyVmFactory;
_propertyInputService = propertyInputService;
LayerEffect = layerEffect;
Children = new ObservableCollection<ViewModelBase>();
LayerPropertyGroup = layerPropertyGroup;
TreeGroupViewModel = layerPropertyVmFactory.TreeGroupViewModel(this);
TimelineGroupViewModel = layerPropertyVmFactory.TimelineGroupViewModel(this);
LayerPropertyGroup.VisibilityChanged += LayerPropertyGroupOnVisibilityChanged;
_isVisible = !LayerPropertyGroup.IsHidden;
PopulateChildren();
}
public ObservableCollection<ViewModelBase> Children { get; }
@@ -53,7 +53,7 @@
<StackPanel Orientation="Horizontal"
Margin="0 5"
IsVisible="{Binding GroupType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static viewModel:LayerPropertyGroupType.General}}">
<avalonia:MaterialIcon Kind="HammerWrench" Margin="0 -1 5 0" />
<avalonia:MaterialIcon Kind="HammerWrench" Margin="0 0 5 0" />
<TextBlock ToolTip.Tip="{Binding LayerPropertyGroup.GroupDescription.Description}">General</TextBlock>
</StackPanel>
@@ -61,22 +61,23 @@
<StackPanel Orientation="Horizontal"
Margin="0 5"
IsVisible="{Binding GroupType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static viewModel:LayerPropertyGroupType.Transform}}">
<avalonia:MaterialIcon Kind="TransitConnectionVariant" Margin="0 -1 5 0" />
<avalonia:MaterialIcon Kind="TransitConnectionVariant" Margin="0 0 5 0" />
<TextBlock ToolTip.Tip="{Binding LayerPropertyGroup.GroupDescription.Description}">Transform</TextBlock>
</StackPanel>
<!-- Type: LayerBrushRoot -->
<Grid IsVisible="{Binding GroupType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static viewModel:LayerPropertyGroupType.LayerBrushRoot}}"
Height="29"
ColumnDefinitions="Auto,Auto,Auto,*">
<shared:ArtemisIcon Grid.Column="0"
Icon="{Binding LayerBrush.Descriptor.Icon}"
Width="16"
Height="16"
Margin="0 5 5 0" />
Margin="0 0 5 0" />
<TextBlock Grid.Column="1"
ToolTip.Tip="{Binding LayerBrush.Descriptor.Description}"
Margin="0 5 0 0">
Brush -&#160;
Margin="0 5 5 0">
Brush -
</TextBlock>
<TextBlock Grid.Column="2"
Text="{Binding LayerBrush.Descriptor.DisplayName}"
@@ -101,8 +102,8 @@
</Grid>
<!-- Type: LayerEffectRoot -->
<Grid Height="24"
IsVisible="{Binding GroupType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static viewModel:LayerPropertyGroupType.LayerEffectRoot}}"
<Grid IsVisible="{Binding GroupType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static viewModel:LayerPropertyGroupType.LayerEffectRoot}}"
Height="29"
ColumnDefinitions="Auto,Auto,Auto,Auto,*,Auto">
<shared:ArtemisIcon
Grid.Column="0"
@@ -110,7 +111,7 @@
Icon="{Binding LayerEffect.Descriptor.Icon}"
Width="16"
Height="16"
Margin="0 5 5 0"
Margin="0 0 5 0"
Background="Transparent" />
<TextBlock Grid.Column="1" ToolTip.Tip="{Binding LayerEffect.Descriptor.Description}" Margin="0 5 0 0">
Effect
@@ -133,18 +134,23 @@
Margin="0 5"
IsVisible="{Binding LayerEffect.Name, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
<StackPanel Grid.Column="5" Orientation="Horizontal">
<ToggleButton
Classes="icon-button"
ToolTip.Tip="Toggle suspended state"
Width="18"
Height="18"
IsChecked="{Binding !LayerEffect.Suspended}"
VerticalAlignment="Center" Padding="-25"
Margin="5 0"
Command="{Binding SuspendedToggled}">
<avalonia:MaterialIcon Kind="Eye" Height="13" Width="13" />
</ToggleButton>
<StackPanel Grid.Column="5" Orientation="Horizontal" Spacing="2">
<Button Classes="icon-button"
ToolTip.Tip="Toggle suspended state"
Width="24"
Height="24"
VerticalAlignment="Center"
Command="{Binding SuspendedToggled}">
<avalonia:MaterialIcon Kind="EyeOff" Height="16" Width="16" IsVisible="True" />
</Button>
<Button Classes="icon-button"
ToolTip.Tip="Toggle suspended state"
Width="24"
Height="24"
VerticalAlignment="Center"
Command="{Binding SuspendedToggled}">
<avalonia:MaterialIcon Kind="Eye" Height="16" Width="16" IsVisible="True" />
</Button>
<Button Classes="icon-button"
ToolTip.Tip="Rename"
Width="24"
@@ -268,7 +268,7 @@
Items="{CompiledBinding RenderScales}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding [0]}" />
<TextBlock Text="{CompiledBinding Display}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
@@ -294,7 +294,7 @@
Items="{CompiledBinding TargetFrameRates}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding [0]}" />
<TextBlock Text="{CompiledBinding Display}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
@@ -80,21 +80,21 @@ namespace Artemis.UI.Screens.Settings
"Vulkan"
};
public ObservableCollection<(string, double)> RenderScales { get; } = new()
public ObservableCollection<RenderSettingViewModel> RenderScales { get; } = new()
{
new ValueTuple<string, double>("25%", 0.25),
new ValueTuple<string, double>("50%", 0.5),
new ValueTuple<string, double>("100%", 1)
new RenderSettingViewModel("25%", 0.25),
new RenderSettingViewModel("50%", 0.5),
new RenderSettingViewModel("100%", 1)
};
public ObservableCollection<(string, int)> TargetFrameRates { get; } = new()
public ObservableCollection<RenderSettingViewModel> TargetFrameRates { get; } = new()
{
new ValueTuple<string, int>("10 FPS", 10),
new ValueTuple<string, int>("20 FPS", 20),
new ValueTuple<string, int>("30 FPS", 30),
new ValueTuple<string, int>("45 FPS", 45),
new ValueTuple<string, int>("60 FPS (lol)", 60),
new ValueTuple<string, int>("144 FPS (omegalol)", 144)
new RenderSettingViewModel("10 FPS", 10),
new RenderSettingViewModel("20 FPS", 20),
new RenderSettingViewModel("30 FPS", 30),
new RenderSettingViewModel("45 FPS", 45),
new RenderSettingViewModel("60 FPS (lol)", 60),
new RenderSettingViewModel("144 FPS (omegalol)", 144)
};
public LayerBrushDescriptor? SelectedLayerBrushDescriptor
@@ -106,21 +106,23 @@ namespace Artemis.UI.Screens.Settings
}
}
public (string, double)? SelectedRenderScale
public RenderSettingViewModel? SelectedRenderScale
{
get => RenderScales.FirstOrDefault(s => Math.Abs(s.Item2 - CoreRenderScale.Value) < 0.01);
get => RenderScales.FirstOrDefault(s => Math.Abs(s.Value - CoreRenderScale.Value) < 0.01);
set
{
if (value != null) CoreRenderScale.Value = value.Value.Item2;
if (value != null)
CoreRenderScale.Value = value.Value;
}
}
public (string, int)? SelectedTargetFrameRate
public RenderSettingViewModel? SelectedTargetFrameRate
{
get => TargetFrameRates.FirstOrDefault(s => s.Item2 == CoreTargetFrameRate.Value);
get => TargetFrameRates.FirstOrDefault(s => Math.Abs(s.Value - CoreTargetFrameRate.Value) < 0.01);
set
{
if (value != null) CoreTargetFrameRate.Value = value.Value.Item2;
if (value != null)
CoreTargetFrameRate.Value = (int) value.Value;
}
}
@@ -0,0 +1,13 @@
namespace Artemis.UI.Screens.Settings;
public class RenderSettingViewModel
{
public RenderSettingViewModel(string display, double value)
{
Display = display;
Value = value;
}
public string Display { get; }
public double Value { get; }
}