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

Profile editor - Added effect renaming

Core - Fixed effect loading
Core - Fixed some effects not applying on per-LED brushes
This commit is contained in:
Robert 2022-04-23 21:12:06 +02:00
parent 2bf36fbf20
commit cd8656cb0d
18 changed files with 292 additions and 79 deletions

View File

@ -18,9 +18,12 @@
<entry key="Artemis.UI/DefaultTypes/PropertyInput/ColorGradientPropertyInputView.axaml" value="Artemis.UI.Windows/Artemis.UI.Windows.csproj" /> <entry key="Artemis.UI/DefaultTypes/PropertyInput/ColorGradientPropertyInputView.axaml" value="Artemis.UI.Windows/Artemis.UI.Windows.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Dialogs/AddEffectView.axaml" value="Artemis.UI.Windows/Artemis.UI.Windows.csproj" /> <entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Dialogs/AddEffectView.axaml" value="Artemis.UI.Windows/Artemis.UI.Windows.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertiesView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertiesView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/ContentDialogs/LayerEffectRenameView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/ContentDialogs/SidebarCategoryEditView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreeGroupView.axaml" value="Artemis.UI.Windows/Artemis.UI.Windows.csproj" /> <entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreeGroupView.axaml" value="Artemis.UI.Windows/Artemis.UI.Windows.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreePropertyView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreePropertyView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/Settings/Tabs/GeneralTabView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI/Screens/Settings/Tabs/GeneralTabView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/Sidebar/ContentDialogs/SidebarCategoryEditView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Avalonia/Artemis.UI/Screens/Debugger/Tabs/Render/RenderDebugView.axaml" value="Avalonia/Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Avalonia/Artemis.UI/Screens/Debugger/Tabs/Render/RenderDebugView.axaml" value="Avalonia/Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Avalonia/Artemis.UI/Styles/Artemis.axaml" value="Avalonia/Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Avalonia/Artemis.UI/Styles/Artemis.axaml" value="Avalonia/Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
</map> </map>

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Reflection;
using Artemis.Core.JsonConverters; using Artemis.Core.JsonConverters;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.Core.Services.Core; using Artemis.Core.Services.Core;
@ -14,6 +15,11 @@ namespace Artemis.Core
/// </summary> /// </summary>
public static class Constants public static class Constants
{ {
/// <summary>
/// The Artemis.Core assembly
/// </summary>
public static readonly Assembly CoreAssembly = typeof(Constants).Assembly;
/// <summary> /// <summary>
/// The full path to the Artemis application folder /// The full path to the Artemis application folder
/// </summary> /// </summary>

View File

@ -627,13 +627,12 @@ namespace Artemis.Core
if (LayerBrush == null) if (LayerBrush == null)
throw new ArtemisCoreException("The layer is not yet ready for rendering"); throw new ArtemisCoreException("The layer is not yet ready for rendering");
using SKAutoCanvasRestore _ = new(canvas);
foreach (BaseLayerEffect baseLayerEffect in LayerEffects) foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
{ {
if (!baseLayerEffect.Suspended) if (!baseLayerEffect.Suspended)
baseLayerEffect.InternalPreProcess(canvas, bounds, layerPaint); baseLayerEffect.InternalPreProcess(canvas, bounds, layerPaint);
} }
using SKAutoCanvasRestore _ = new(canvas);
canvas.ClipPath(renderPath); canvas.ClipPath(renderPath);
// Restore the blend mode before doing the actual render // Restore the blend mode before doing the actual render

View File

@ -35,7 +35,6 @@ namespace Artemis.Core.LayerBrushes
canvas.SetMatrix(canvas.TotalMatrix.PreConcat(Layer.GetTransformMatrix(true, false, false, true).Invert())); canvas.SetMatrix(canvas.TotalMatrix.PreConcat(Layer.GetTransformMatrix(true, false, false, true).Invert()));
using SKPath pointsPath = new(); using SKPath pointsPath = new();
using SKPaint ledPaint = new();
foreach (ArtemisLed artemisLed in Layer.Leds) foreach (ArtemisLed artemisLed in Layer.Leds)
{ {
pointsPath.AddPoly(new[] pointsPath.AddPoly(new[]
@ -61,7 +60,7 @@ namespace Artemis.Core.LayerBrushes
continue; continue;
// Let the brush determine the color // Let the brush determine the color
ledPaint.Color = GetColor(artemisLed, renderPoint); paint.Color = GetColor(artemisLed, renderPoint);
SKRect ledRectangle = SKRect.Create( SKRect ledRectangle = SKRect.Create(
artemisLed.AbsoluteRectangle.Left - Layer.Bounds.Left, artemisLed.AbsoluteRectangle.Left - Layer.Bounds.Left,
@ -70,7 +69,7 @@ namespace Artemis.Core.LayerBrushes
artemisLed.AbsoluteRectangle.Height artemisLed.AbsoluteRectangle.Height
); );
canvas.DrawRect(ledRectangle, ledPaint); canvas.DrawRect(ledRectangle, paint);
} }
}, "Failed to render"); }, "Failed to render");
} }

View File

@ -222,8 +222,8 @@ namespace Artemis.Core.LayerEffects
/// <inheritdoc /> /// <inheritdoc />
public void Load() public void Load()
{ {
Name = LayerEffectEntity.Name;
HasBeenRenamed = LayerEffectEntity.HasBeenRenamed; HasBeenRenamed = LayerEffectEntity.HasBeenRenamed;
Name = HasBeenRenamed ? LayerEffectEntity.Name : Descriptor.DisplayName;
Order = LayerEffectEntity.Order; Order = LayerEffectEntity.Order;
} }

View File

@ -87,6 +87,7 @@ public class LayerEffectDescriptor
else else
{ {
effect.LayerEffectEntity = new LayerEffectEntity(); effect.LayerEffectEntity = new LayerEffectEntity();
effect.Name = DisplayName;
effect.Initialize(); effect.Initialize();
effect.Save(); effect.Save();
} }

View File

@ -51,7 +51,7 @@ namespace Artemis.Core
{ {
lock (Registrations) lock (Registrations)
{ {
return Registrations.FirstOrDefault(d => d.PluginFeature.Id == providerId && d.LayerEffectDescriptor.LayerEffectType?.Name == typeName); return Registrations.FirstOrDefault(d => d.PluginFeature.Id == providerId && d.LayerEffectDescriptor.LayerEffectType?.FullName == typeName);
} }
} }

View File

@ -0,0 +1,48 @@
using System;
using Artemis.Core;
using Artemis.Core.LayerEffects;
namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
/// <summary>
/// Represents a profile editor command that can be used to remove a layer effect from a profile element.
/// </summary>
public class RemoveLayerEffect : IProfileEditorCommand, IDisposable
{
private readonly RenderProfileElement _renderProfileElement;
private readonly BaseLayerEffect _layerEffect;
private bool _executed;
/// <summary>
/// Creates a new instance of the <see cref="RemoveLayerEffect"/> class.
/// </summary>
public RemoveLayerEffect(BaseLayerEffect layerEffect)
{
_renderProfileElement = layerEffect.ProfileElement;
_layerEffect = layerEffect;
}
/// <inheritdoc />
public string DisplayName => "Remove layer effect";
/// <inheritdoc />
public void Execute()
{
_renderProfileElement.RemoveLayerEffect(_layerEffect);
_executed = true;
}
/// <inheritdoc />
public void Undo()
{
_renderProfileElement.AddLayerEffect(_layerEffect);
_executed = false;
}
/// <inheritdoc />
public void Dispose()
{
if (_executed)
_layerEffect.Dispose();
}
}

View File

@ -0,0 +1,44 @@
using System;
using Artemis.Core;
using Artemis.Core.LayerEffects;
namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
/// <summary>
/// Represents a profile editor command that can be used to rename a layer effect
/// </summary>
public class RenameLayerEffect : IProfileEditorCommand
{
private readonly BaseLayerEffect _layerEffect;
private readonly string _name;
private readonly string _oldName;
private readonly bool _wasRenamed;
/// <summary>
/// Creates a new instance of the <see cref="RenameLayerEffect"/> class.
/// </summary>
public RenameLayerEffect(BaseLayerEffect layerEffect, string name)
{
_layerEffect = layerEffect;
_name = name;
_oldName = layerEffect.Name;
_wasRenamed = layerEffect.HasBeenRenamed;
}
/// <inheritdoc />
public string DisplayName => "Rename layer effect";
/// <inheritdoc />
public void Execute()
{
_layerEffect.Name = _name;
_layerEffect.HasBeenRenamed = true;
}
/// <inheritdoc />
public void Undo()
{
_layerEffect.Name = _oldName;
_layerEffect.HasBeenRenamed = _wasRenamed;
}
}

View File

@ -68,6 +68,14 @@
<Compile Update="Screens\VisualScripting\NodeScriptWindowView.axaml.cs"> <Compile Update="Screens\VisualScripting\NodeScriptWindowView.axaml.cs">
<DependentUpon>NodeScriptWindowView.axaml</DependentUpon> <DependentUpon>NodeScriptWindowView.axaml</DependentUpon>
</Compile> </Compile>
<Compile Update="Screens\ProfileEditor\Panels\Properties\Tree\ContentDialogs\LayerEffectRenameView.axaml.cs">
<DependentUpon>LayerEffectRenameView.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="Screens\ProfileEditor\Panels\Properties\Tree\ContentDialogs\SidebarCategoryEditView.axaml.cs">
<DependentUpon>SidebarCategoryEditView.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<AvaloniaXaml Update="DefaultTypes\PropertyInput\StringPropertyInputView.axaml"> <AvaloniaXaml Update="DefaultTypes\PropertyInput\StringPropertyInputView.axaml">

View File

@ -1,4 +1,5 @@
using Artemis.Core; using System;
using Artemis.Core;
using Artemis.UI.Shared.Services.ProfileEditor; using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.PropertyInput; using Artemis.UI.Shared.Services.PropertyInput;
using ReactiveUI; using ReactiveUI;
@ -14,17 +15,17 @@ public class SKPointPropertyInputViewModel : PropertyInputViewModel<SKPoint>
{ {
if (LayerProperty.PropertyDescription.MinInputValue.IsNumber()) if (LayerProperty.PropertyDescription.MinInputValue.IsNumber())
{ {
this.ValidationRule(vm => vm.X, i => i >= (float) LayerProperty.PropertyDescription.MinInputValue, this.ValidationRule(vm => vm.X, i => i >= Convert.ToSingle(LayerProperty.PropertyDescription.MinInputValue),
$"X must be equal to or greater than {LayerProperty.PropertyDescription.MinInputValue}."); $"X must be equal to or greater than {LayerProperty.PropertyDescription.MinInputValue}.");
this.ValidationRule(vm => vm.Y, i => i >= (float) LayerProperty.PropertyDescription.MinInputValue, this.ValidationRule(vm => vm.Y, i => i >= Convert.ToSingle(LayerProperty.PropertyDescription.MinInputValue),
$"Y must be equal to or greater than {LayerProperty.PropertyDescription.MinInputValue}."); $"Y must be equal to or greater than {LayerProperty.PropertyDescription.MinInputValue}.");
} }
if (LayerProperty.PropertyDescription.MaxInputValue.IsNumber()) if (LayerProperty.PropertyDescription.MaxInputValue.IsNumber())
{ {
this.ValidationRule(vm => vm.X, i => i <= (float) LayerProperty.PropertyDescription.MaxInputValue, this.ValidationRule(vm => vm.X, i => i <= Convert.ToSingle(LayerProperty.PropertyDescription.MaxInputValue),
$"X must be equal to or smaller than {LayerProperty.PropertyDescription.MaxInputValue}."); $"X must be equal to or smaller than {LayerProperty.PropertyDescription.MaxInputValue}.");
this.ValidationRule(vm => vm.Y, i => i <= (float) LayerProperty.PropertyDescription.MaxInputValue, this.ValidationRule(vm => vm.Y, i => i <= Convert.ToSingle(LayerProperty.PropertyDescription.MaxInputValue),
$"Y must be equal to or smaller than {LayerProperty.PropertyDescription.MaxInputValue}."); $"Y must be equal to or smaller than {LayerProperty.PropertyDescription.MaxInputValue}.");
} }
} }

View File

@ -1,4 +1,5 @@
using Artemis.Core; using System;
using Artemis.Core;
using Artemis.UI.Shared.Services.ProfileEditor; using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.PropertyInput; using Artemis.UI.Shared.Services.PropertyInput;
using ReactiveUI; using ReactiveUI;
@ -16,17 +17,17 @@ public class SKSizePropertyInputViewModel : PropertyInputViewModel<SKSize>
{ {
if (LayerProperty.PropertyDescription.MinInputValue.IsNumber()) if (LayerProperty.PropertyDescription.MinInputValue.IsNumber())
{ {
this.ValidationRule(vm => vm.Width, i => i >= (float) LayerProperty.PropertyDescription.MinInputValue, this.ValidationRule(vm => vm.Width, i => i >= Convert.ToSingle(LayerProperty.PropertyDescription.MinInputValue),
$"Width must be equal to or greater than {LayerProperty.PropertyDescription.MinInputValue}."); $"Width must be equal to or greater than {LayerProperty.PropertyDescription.MinInputValue}.");
this.ValidationRule(vm => vm.Height, i => i >= (float) LayerProperty.PropertyDescription.MinInputValue, this.ValidationRule(vm => vm.Height, i => i >= Convert.ToSingle(LayerProperty.PropertyDescription.MinInputValue),
$"Height must be equal to or greater than {LayerProperty.PropertyDescription.MinInputValue}."); $"Height must be equal to or greater than {LayerProperty.PropertyDescription.MinInputValue}.");
} }
if (LayerProperty.PropertyDescription.MaxInputValue.IsNumber()) if (LayerProperty.PropertyDescription.MaxInputValue.IsNumber())
{ {
this.ValidationRule(vm => vm.Width, i => i <= (float) LayerProperty.PropertyDescription.MaxInputValue, this.ValidationRule(vm => vm.Width, i => i <= Convert.ToSingle(LayerProperty.PropertyDescription.MaxInputValue),
$"Width must be equal to or smaller than {LayerProperty.PropertyDescription.MaxInputValue}."); $"Width must be equal to or smaller than {LayerProperty.PropertyDescription.MaxInputValue}.");
this.ValidationRule(vm => vm.Height, i => i <= (float) LayerProperty.PropertyDescription.MaxInputValue, this.ValidationRule(vm => vm.Height, i => i <= Convert.ToSingle(LayerProperty.PropertyDescription.MaxInputValue),
$"Height must be equal to or smaller than {LayerProperty.PropertyDescription.MaxInputValue}."); $"Height must be equal to or smaller than {LayerProperty.PropertyDescription.MaxInputValue}.");
} }
} }

View File

@ -117,7 +117,8 @@ public class PropertyGroupViewModel : ViewModelBase, IDisposable
{ {
// Get all properties and property groups and create VMs for them // Get all properties and property groups and create VMs for them
// The group has methods for getting this without reflection but then we lose the order of the properties as they are defined on the group // The group has methods for getting this without reflection but then we lose the order of the properties as they are defined on the group
foreach (PropertyInfo propertyInfo in LayerPropertyGroup.GetType().GetProperties()) // Sorting is done to ensure properties defined by the Core (such as on layers) are always on top.
foreach (PropertyInfo propertyInfo in LayerPropertyGroup.GetType().GetProperties().OrderBy(p => p.DeclaringType?.Assembly != Constants.CoreAssembly))
{ {
if (Attribute.IsDefined(propertyInfo, typeof(LayerPropertyIgnoreAttribute))) if (Attribute.IsDefined(propertyInfo, typeof(LayerPropertyIgnoreAttribute)))
continue; continue;

View File

@ -0,0 +1,15 @@
<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:contentDialogs="clr-namespace:Artemis.UI.Screens.ProfileEditor.Properties.Tree.ContentDialogs"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.Properties.Tree.ContentDialogs.LayerEffectRenameView"
x:DataType="contentDialogs:LayerEffectRenameViewModel">
<StackPanel>
<StackPanel.KeyBindings>
<KeyBinding Gesture="Enter" Command="{CompiledBinding Confirm}" />
</StackPanel.KeyBindings>
<TextBox Name="NameTextBox" Text="{CompiledBinding LayerEffectName}" Watermark="Layer effect name"/>
</StackPanel>
</UserControl>

View File

@ -0,0 +1,35 @@
using System.Threading.Tasks;
using Artemis.UI.Shared.Extensions;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
using Avalonia.Threading;
using ReactiveUI;
namespace Artemis.UI.Screens.ProfileEditor.Properties.Tree.ContentDialogs;
public class LayerEffectRenameView : ReactiveUserControl<LayerEffectRenameViewModel>
{
public LayerEffectRenameView()
{
InitializeComponent();
this.WhenActivated(_ =>
{
this.ClearAllDataValidationErrors();
Dispatcher.UIThread.Post(DelayedAutoFocus);
});
}
private async void DelayedAutoFocus()
{
// Don't ask
await Task.Delay(200);
this.Get<TextBox>("NameTextBox").SelectAll();
this.Get<TextBox>("NameTextBox").Focus();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}

View File

@ -0,0 +1,45 @@
using System.Reactive;
using Artemis.Core.LayerEffects;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
using FluentAvalonia.UI.Controls;
using ReactiveUI;
using ReactiveUI.Validation.Extensions;
namespace Artemis.UI.Screens.ProfileEditor.Properties.Tree.ContentDialogs
{
public class LayerEffectRenameViewModel : ContentDialogViewModelBase
{
private readonly IProfileEditorService _profileEditorService;
private readonly BaseLayerEffect _layerEffect;
private string? _layerEffectName;
public LayerEffectRenameViewModel(IProfileEditorService profileEditorService, BaseLayerEffect layerEffect)
{
_profileEditorService = profileEditorService;
_layerEffect = layerEffect;
_layerEffectName = layerEffect.Name;
Confirm = ReactiveCommand.Create(ExecuteConfirm, ValidationContext.Valid);
this.ValidationRule(vm => vm.LayerEffectName, categoryName => !string.IsNullOrWhiteSpace(categoryName), "You must specify a valid name");
}
public string? LayerEffectName
{
get => _layerEffectName;
set => this.RaiseAndSetIfChanged(ref _layerEffectName, value);
}
public ReactiveCommand<Unit, Unit> Confirm { get; }
private void ExecuteConfirm()
{
if (LayerEffectName == null)
return;
_profileEditorService.ExecuteCommand(new RenameLayerEffect(_layerEffect, LayerEffectName));
ContentDialog?.Hide(ContentDialogResult.Primary);
}
}
}

View File

@ -9,7 +9,8 @@
xmlns:properties="clr-namespace:Artemis.UI.Screens.ProfileEditor.Properties" xmlns:properties="clr-namespace:Artemis.UI.Screens.ProfileEditor.Properties"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared;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.ProfileEditor.Properties.Tree.TreeGroupView"> x:Class="Artemis.UI.Screens.ProfileEditor.Properties.Tree.TreeGroupView"
x:DataType="viewModel:TreeGroupViewModel">
<UserControl.Resources> <UserControl.Resources>
<converters:PropertyTreeMarginConverter x:Key="PropertyTreeMarginConverter" Length="20" /> <converters:PropertyTreeMarginConverter x:Key="PropertyTreeMarginConverter" Length="20" />
<sharedConverters:EnumToBooleanConverter x:Key="EnumBoolConverter" /> <sharedConverters:EnumToBooleanConverter x:Key="EnumBoolConverter" />
@ -26,8 +27,8 @@
Height="29"> Height="29">
<Grid Margin="{Binding Converter={StaticResource PropertyTreeMarginConverter}}" ColumnDefinitions="19,*"> <Grid Margin="{Binding Converter={StaticResource PropertyTreeMarginConverter}}" ColumnDefinitions="19,*">
<avalonia:MaterialIcon Classes.chevron-collapsed="{Binding !PropertyGroupViewModel.IsExpanded}" <avalonia:MaterialIcon Classes.chevron-collapsed="{CompiledBinding !PropertyGroupViewModel.IsExpanded}"
IsVisible="{Binding PropertyGroupViewModel.HasChildren}" IsVisible="{CompiledBinding PropertyGroupViewModel.HasChildren}"
Kind="ChevronDown" Kind="ChevronDown"
Grid.Column="0" Grid.Column="0"
Margin="5 0" Margin="5 0"
@ -42,59 +43,59 @@
<StackPanel Grid.Column="1"> <StackPanel Grid.Column="1">
<!-- Type: None --> <!-- Type: None -->
<TextBlock Text="{Binding LayerPropertyGroup.GroupDescription.Name}" <TextBlock Text="{CompiledBinding LayerPropertyGroup.GroupDescription.Name}"
ToolTip.Tip="{Binding LayerPropertyGroup.GroupDescription.Description}" ToolTip.Tip="{CompiledBinding LayerPropertyGroup.GroupDescription.Description}"
VerticalAlignment="Center" VerticalAlignment="Center"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Margin="3 5 0 5" Margin="3 5 0 5"
IsVisible="{Binding GroupType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static viewModel:LayerPropertyGroupType.None}}" /> IsVisible="{CompiledBinding GroupType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static viewModel:LayerPropertyGroupType.None}}" />
<!-- Type: General --> <!-- Type: General -->
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Margin="0 5" Margin="0 5"
IsVisible="{Binding GroupType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static viewModel:LayerPropertyGroupType.General}}"> IsVisible="{CompiledBinding GroupType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static viewModel:LayerPropertyGroupType.General}}">
<avalonia:MaterialIcon Kind="HammerWrench" Margin="0 0 5 0" /> <avalonia:MaterialIcon Kind="HammerWrench" Margin="0 0 5 0" />
<TextBlock ToolTip.Tip="{Binding LayerPropertyGroup.GroupDescription.Description}">General</TextBlock> <TextBlock ToolTip.Tip="{CompiledBinding LayerPropertyGroup.GroupDescription.Description}">General</TextBlock>
</StackPanel> </StackPanel>
<!-- Type: Transform --> <!-- Type: Transform -->
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Margin="0 5" Margin="0 5"
IsVisible="{Binding GroupType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static viewModel:LayerPropertyGroupType.Transform}}"> IsVisible="{CompiledBinding GroupType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static viewModel:LayerPropertyGroupType.Transform}}">
<avalonia:MaterialIcon Kind="TransitConnectionVariant" Margin="0 0 5 0" /> <avalonia:MaterialIcon Kind="TransitConnectionVariant" Margin="0 0 5 0" />
<TextBlock ToolTip.Tip="{Binding LayerPropertyGroup.GroupDescription.Description}">Transform</TextBlock> <TextBlock ToolTip.Tip="{CompiledBinding LayerPropertyGroup.GroupDescription.Description}">Transform</TextBlock>
</StackPanel> </StackPanel>
<!-- Type: LayerBrushRoot --> <!-- Type: LayerBrushRoot -->
<Grid IsVisible="{Binding GroupType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static viewModel:LayerPropertyGroupType.LayerBrushRoot}}" <Grid IsVisible="{CompiledBinding GroupType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static viewModel:LayerPropertyGroupType.LayerBrushRoot}}"
Height="29" Height="29"
ColumnDefinitions="Auto,Auto,Auto,*"> ColumnDefinitions="Auto,Auto,Auto,*">
<shared:ArtemisIcon Grid.Column="0" <shared:ArtemisIcon Grid.Column="0"
Icon="{Binding LayerBrush.Descriptor.Icon}" Icon="{CompiledBinding LayerBrush.Descriptor.Icon}"
Width="16" Width="16"
Height="16" Height="16"
Margin="0 0 5 0" /> Margin="0 0 5 0" />
<TextBlock Grid.Column="1" <TextBlock Grid.Column="1"
ToolTip.Tip="{Binding LayerBrush.Descriptor.Description}" ToolTip.Tip="{CompiledBinding LayerBrush.Descriptor.Description}"
Margin="0 5 5 0"> Margin="0 5 5 0">
Brush - Brush -
</TextBlock> </TextBlock>
<TextBlock Grid.Column="2" <TextBlock Grid.Column="2"
Text="{Binding LayerBrush.Descriptor.DisplayName}" Text="{CompiledBinding LayerBrush.Descriptor.DisplayName}"
ToolTip.Tip="{Binding LayerBrush.Descriptor.Description}" ToolTip.Tip="{CompiledBinding LayerBrush.Descriptor.Description}"
Margin="0 5 0 0" /> Margin="0 5 0 0" />
<StackPanel Grid.Column="3" <StackPanel Grid.Column="3"
Orientation="Horizontal" Orientation="Horizontal"
HorizontalAlignment="Right" HorizontalAlignment="Right"
IsVisible="{Binding LayerBrush.ConfigurationDialog, Converter={x:Static ObjectConverters.IsNotNull}}"> IsVisible="{CompiledBinding LayerBrush.ConfigurationDialog, Converter={x:Static ObjectConverters.IsNotNull}}">
<TextBlock VerticalAlignment="Center">Extra options available!</TextBlock> <TextBlock VerticalAlignment="Center">Extra options available!</TextBlock>
<avalonia:MaterialIcon Kind="ChevronRight" VerticalAlignment="Center"> <avalonia:MaterialIcon Kind="ChevronRight" VerticalAlignment="Center">
<avalonia:MaterialIcon.RenderTransform> <avalonia:MaterialIcon.RenderTransform>
<TranslateTransform X="0" /> <TranslateTransform X="0" />
</avalonia:MaterialIcon.RenderTransform> </avalonia:MaterialIcon.RenderTransform>
</avalonia:MaterialIcon> </avalonia:MaterialIcon>
<Button Classes="icon-button" ToolTip.Tip="Open brush settings" Width="24" Height="24" HorizontalAlignment="Right" Command="{Binding OpenBrushSettings}"> <Button Classes="icon-button" ToolTip.Tip="Open brush settings" Width="24" Height="24" HorizontalAlignment="Right" Command="{CompiledBinding OpenBrushSettings}">
<avalonia:MaterialIcon Kind="Settings" Height="16" Width="16" /> <avalonia:MaterialIcon Kind="Settings" Height="16" Width="16" />
</Button> </Button>
</StackPanel> </StackPanel>
@ -102,61 +103,45 @@
</Grid> </Grid>
<!-- Type: LayerEffectRoot --> <!-- Type: LayerEffectRoot -->
<Grid IsVisible="{Binding GroupType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static viewModel:LayerPropertyGroupType.LayerEffectRoot}}" <Grid IsVisible="{CompiledBinding GroupType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static viewModel:LayerPropertyGroupType.LayerEffectRoot}}"
Height="29" Height="29"
ColumnDefinitions="Auto,Auto,Auto,Auto,*,Auto"> ColumnDefinitions="Auto,Auto,Auto,Auto,*,Auto">
<shared:ArtemisIcon <shared:ArtemisIcon
Grid.Column="0" Grid.Column="0"
Cursor="SizeNorthSouth" Cursor="SizeNorthSouth"
Icon="{Binding LayerEffect.Descriptor.Icon}" Icon="{CompiledBinding LayerEffect.Descriptor.Icon}"
Width="16" Width="16"
Height="16" Height="16"
Margin="0 0 5 0" Margin="0 0 5 0"
Background="Transparent" /> Background="Transparent" />
<TextBlock Grid.Column="1" ToolTip.Tip="{Binding LayerEffect.Descriptor.Description}" Margin="0 5 0 0"> <TextBlock Grid.Column="1" ToolTip.Tip="{CompiledBinding LayerEffect.Descriptor.Description}" Margin="0 5 0 0">
Effect Effect
</TextBlock> </TextBlock>
<TextBlock Grid.Column="2" <TextBlock Grid.Column="2"
ToolTip.Tip="{Binding LayerEffect.Descriptor.Description}" ToolTip.Tip="{CompiledBinding LayerEffect.Descriptor.Description}"
Margin="3 5"> Margin="3 5">
- -
</TextBlock> </TextBlock>
<!-- Show either the descriptors display name or, if set, the effect name --> <!-- Show either the descriptors display name or, if set, the effect name -->
<TextBlock Grid.Column="3" <TextBlock Grid.Column="3"
Text="{Binding LayerEffect.Descriptor.DisplayName}" Text="{CompiledBinding LayerEffect.Descriptor.DisplayName}"
ToolTip.Tip="{Binding LayerEffect.Descriptor.Description}" ToolTip.Tip="{CompiledBinding LayerEffect.Descriptor.Description}"
Margin="0 5" Margin="0 5"
IsVisible="{Binding !LayerEffect.Name, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" /> IsVisible="{CompiledBinding !LayerEffect.HasBeenRenamed}" />
<TextBlock Grid.Column="4" <TextBlock Grid.Column="4"
Text="{Binding LayerEffect.Name}" Text="{CompiledBinding LayerEffect.Name}"
ToolTip.Tip="{Binding LayerEffect.Descriptor.Description}" ToolTip.Tip="{CompiledBinding LayerEffect.Descriptor.Description}"
Margin="0 5" Margin="0 5"
IsVisible="{Binding LayerEffect.Name, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" /> IsVisible="{CompiledBinding LayerEffect.HasBeenRenamed}" />
<StackPanel Grid.Column="5" Orientation="Horizontal" Spacing="2"> <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" <Button Classes="icon-button"
ToolTip.Tip="Rename" ToolTip.Tip="Rename"
Width="24" Width="24"
Height="24" Height="24"
VerticalAlignment="Center" VerticalAlignment="Center"
Command="{Binding RenameEffect}"> Command="{CompiledBinding RenameEffect}">
<avalonia:MaterialIcon Kind="RenameBox" Height="16" Width="16" /> <avalonia:MaterialIcon Kind="RenameBox" Height="16" Width="16" />
</Button> </Button>
<Button Classes="icon-button" <Button Classes="icon-button"
@ -164,8 +149,8 @@
Width="24" Width="24"
Height="24" Height="24"
VerticalAlignment="Center" VerticalAlignment="Center"
Command="{Binding OpenEffectSettings}" Command="{CompiledBinding OpenEffectSettings}"
IsVisible="{Binding LayerEffect.ConfigurationDialog, Converter={x:Static ObjectConverters.IsNotNull}}"> IsVisible="{CompiledBinding LayerEffect.ConfigurationDialog, Converter={x:Static ObjectConverters.IsNotNull}}">
<avalonia:MaterialIcon Kind="Settings" Height="16" Width="16" /> <avalonia:MaterialIcon Kind="Settings" Height="16" Width="16" />
</Button> </Button>
<Button Classes="icon-button" <Button Classes="icon-button"
@ -173,7 +158,7 @@
Width="24" Width="24"
Height="24" Height="24"
VerticalAlignment="Center" VerticalAlignment="Center"
Command="{Binding DeleteEffect}"> Command="{CompiledBinding DeleteEffect}">
<avalonia:MaterialIcon Kind="TrashCan" Height="16" Width="16" /> <avalonia:MaterialIcon Kind="TrashCan" Height="16" Width="16" />
</Button> </Button>
</StackPanel> </StackPanel>
@ -186,15 +171,15 @@
Do not bind directly to the PropertyGroupViewModel.Children collection Do not bind directly to the PropertyGroupViewModel.Children collection
Instead use a reference provided by the VM that is null when collapsed, virtualization for noobs Instead use a reference provided by the VM that is null when collapsed, virtualization for noobs
--> -->
<ItemsControl Items="{Binding Children}" <ItemsControl Items="{CompiledBinding Children}"
IsVisible="{Binding PropertyGroupViewModel.IsExpanded}" IsVisible="{CompiledBinding PropertyGroupViewModel.IsExpanded}"
HorizontalAlignment="Stretch"> HorizontalAlignment="Stretch">
<ItemsControl.DataTemplates> <ItemsControl.DataTemplates>
<DataTemplate DataType="properties:PropertyGroupViewModel"> <DataTemplate DataType="properties:PropertyGroupViewModel">
<ContentControl Content="{Binding TreeGroupViewModel}" IsVisible="{Binding IsVisible}" /> <ContentControl Content="{CompiledBinding TreeGroupViewModel}" IsVisible="{CompiledBinding IsVisible}" />
</DataTemplate> </DataTemplate>
<DataTemplate DataType="properties:PropertyViewModel"> <DataTemplate DataType="properties:PropertyViewModel">
<ContentControl Content="{Binding TreePropertyViewModel}" IsVisible="{Binding IsVisible}" /> <ContentControl Content="{CompiledBinding TreePropertyViewModel}" IsVisible="{CompiledBinding IsVisible}" />
</DataTemplate> </DataTemplate>
</ItemsControl.DataTemplates> </ItemsControl.DataTemplates>
</ItemsControl> </ItemsControl>

View File

@ -1,6 +1,7 @@
using System; using System;
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 System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -8,12 +9,15 @@ using Artemis.Core;
using Artemis.Core.LayerBrushes; using Artemis.Core.LayerBrushes;
using Artemis.Core.LayerEffects; using Artemis.Core.LayerEffects;
using Artemis.UI.Exceptions; using Artemis.UI.Exceptions;
using Artemis.UI.Screens.ProfileEditor.Properties.Tree.ContentDialogs;
using Artemis.UI.Screens.ProfileEditor.Properties.Windows; using Artemis.UI.Screens.ProfileEditor.Properties.Windows;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.LayerBrushes; using Artemis.UI.Shared.LayerBrushes;
using Artemis.UI.Shared.LayerEffects; using Artemis.UI.Shared.LayerEffects;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders;
using Artemis.UI.Shared.Services.ProfileEditor; using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
using Ninject; using Ninject;
using Ninject.Parameters; using Ninject.Parameters;
using ReactiveUI; using ReactiveUI;
@ -41,19 +45,25 @@ public class TreeGroupViewModel : ActivatableViewModelBase
}); });
// TODO: Update ProfileElementPropertyGroupViewModel visibility on change (can remove the sub on line 41 as well then) // TODO: Update ProfileElementPropertyGroupViewModel visibility on change (can remove the sub on line 41 as well then)
OpenBrushSettings = ReactiveCommand.CreateFromTask(ExecuteOpenBrushSettings);
OpenEffectSettings = ReactiveCommand.CreateFromTask(ExecuteOpenEffectSettings);
RenameEffect = ReactiveCommand.CreateFromTask(ExecuteRenameEffect);
DeleteEffect = ReactiveCommand.Create(ExecuteDeleteEffect);
} }
public PropertyGroupViewModel PropertyGroupViewModel { get; } public PropertyGroupViewModel PropertyGroupViewModel { get; }
public LayerPropertyGroup LayerPropertyGroup => PropertyGroupViewModel.LayerPropertyGroup; public LayerPropertyGroup LayerPropertyGroup => PropertyGroupViewModel.LayerPropertyGroup;
public BaseLayerBrush? LayerBrush => PropertyGroupViewModel.LayerBrush; public BaseLayerBrush? LayerBrush => PropertyGroupViewModel.LayerBrush;
public BaseLayerEffect? LayerEffect => PropertyGroupViewModel.LayerEffect; public BaseLayerEffect? LayerEffect => PropertyGroupViewModel.LayerEffect;
public LayerPropertyGroupType GroupType { get; private set; }
public ObservableCollection<ViewModelBase>? Children => PropertyGroupViewModel.IsExpanded ? PropertyGroupViewModel.Children : null; public ObservableCollection<ViewModelBase>? Children => PropertyGroupViewModel.IsExpanded ? PropertyGroupViewModel.Children : null;
public LayerPropertyGroupType GroupType { get; private set; } public ReactiveCommand<Unit, Unit> OpenBrushSettings { get; }
public ReactiveCommand<Unit, Unit> OpenEffectSettings { get; }
public ReactiveCommand<Unit, Unit> RenameEffect { get; }
public ReactiveCommand<Unit, Unit> DeleteEffect { get; }
public async Task OpenBrushSettings() private async Task ExecuteOpenBrushSettings()
{ {
if (LayerBrush?.ConfigurationDialog is not LayerBrushConfigurationDialog configurationViewModel) if (LayerBrush?.ConfigurationDialog is not LayerBrushConfigurationDialog configurationViewModel)
return; return;
@ -83,7 +93,7 @@ public class TreeGroupViewModel : ActivatableViewModelBase
} }
} }
public async Task OpenEffectSettings() private async Task ExecuteOpenEffectSettings()
{ {
if (LayerEffect?.ConfigurationDialog is not LayerEffectConfigurationDialog configurationViewModel) if (LayerEffect?.ConfigurationDialog is not LayerEffectConfigurationDialog configurationViewModel)
return; return;
@ -113,14 +123,26 @@ public class TreeGroupViewModel : ActivatableViewModelBase
} }
} }
public async Task RenameEffect() private async Task ExecuteRenameEffect()
{ {
await _windowService.ShowConfirmContentDialog("Not yet implemented", "Try again later :p"); if (LayerEffect == null)
return;
await _windowService.CreateContentDialog()
.WithTitle("Rename layer effect")
.WithViewModel(out LayerEffectRenameViewModel vm, ("layerEffect", LayerEffect))
.HavingPrimaryButton(b => b.WithText("Confirm").WithCommand(vm.Confirm))
.WithCloseButtonText("Cancel")
.WithDefaultButton(ContentDialogButton.Primary)
.ShowAsync();
} }
public async Task DeleteEffect() private void ExecuteDeleteEffect()
{ {
await _windowService.ShowConfirmContentDialog("Not yet implemented", "Try again later :p"); if (LayerEffect == null)
return;
_profileEditorService.ExecuteCommand(new RemoveLayerEffect(LayerEffect));
} }
public double GetDepth() public double GetDepth()