mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Profile editor - Added most properties
This commit is contained in:
parent
c04bff1f48
commit
022beb6a48
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
@ -92,7 +93,7 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">The value to check</param>
|
/// <param name="value">The value to check</param>
|
||||||
/// <returns><see langword="true" /> if the value is of a numeric type, otherwise <see langword="false" /></returns>
|
/// <returns><see langword="true" /> if the value is of a numeric type, otherwise <see langword="false" /></returns>
|
||||||
public static bool IsNumber(this object value)
|
public static bool IsNumber([NotNullWhenAttribute(true)] this object? value)
|
||||||
{
|
{
|
||||||
return value is sbyte
|
return value is sbyte
|
||||||
|| value is byte
|
|| value is byte
|
||||||
|
|||||||
@ -729,7 +729,7 @@ namespace Artemis.Core
|
|||||||
// Ensure the brush reference matches the brush
|
// Ensure the brush reference matches the brush
|
||||||
LayerBrushReference? current = General.BrushReference.BaseValue;
|
LayerBrushReference? current = General.BrushReference.BaseValue;
|
||||||
if (!descriptor.MatchesLayerBrushReference(current))
|
if (!descriptor.MatchesLayerBrushReference(current))
|
||||||
General.BrushReference.BaseValue = new LayerBrushReference(descriptor);
|
General.BrushReference.SetCurrentValue(new LayerBrushReference(descriptor), null);
|
||||||
|
|
||||||
ActivateLayerBrush();
|
ActivateLayerBrush();
|
||||||
}
|
}
|
||||||
@ -760,6 +760,10 @@ namespace Artemis.Core
|
|||||||
: null;
|
: null;
|
||||||
descriptor?.CreateInstance(this);
|
descriptor?.CreateInstance(this);
|
||||||
|
|
||||||
|
General.ShapeType.IsHidden = LayerBrush != null && !LayerBrush.SupportsTransformation;
|
||||||
|
General.BlendMode.IsHidden = LayerBrush != null && !LayerBrush.SupportsTransformation;
|
||||||
|
Transform.IsHidden = LayerBrush != null && !LayerBrush.SupportsTransformation;
|
||||||
|
|
||||||
OnLayerBrushUpdated();
|
OnLayerBrushUpdated();
|
||||||
ClearBrokenState("Failed to initialize layer brush");
|
ClearBrokenState("Failed to initialize layer brush");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,44 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.Core.LayerBrushes;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a profile editor command that can be used to change the brush of a layer.
|
||||||
|
/// </summary>
|
||||||
|
public class ChangeLayerBrush : IProfileEditorCommand
|
||||||
|
{
|
||||||
|
private readonly Layer _layer;
|
||||||
|
private readonly LayerBrushDescriptor _layerBrushDescriptor;
|
||||||
|
private readonly LayerBrushDescriptor? _previousDescriptor;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="ChangeLayerBrush" /> class.
|
||||||
|
/// </summary>
|
||||||
|
public ChangeLayerBrush(Layer layer, LayerBrushDescriptor layerBrushDescriptor)
|
||||||
|
{
|
||||||
|
_layer = layer;
|
||||||
|
_layerBrushDescriptor = layerBrushDescriptor;
|
||||||
|
_previousDescriptor = layer.LayerBrush?.Descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Implementation of IProfileEditorCommand
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string DisplayName => "Change layer brush";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Execute()
|
||||||
|
{
|
||||||
|
_layer.ChangeLayerBrush(_layerBrushDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Undo()
|
||||||
|
{
|
||||||
|
if (_previousDescriptor != null)
|
||||||
|
_layer.ChangeLayerBrush(_previousDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@ -6,6 +6,7 @@ using Artemis.UI.Shared.Services.ProfileEditor;
|
|||||||
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
||||||
using Avalonia.Controls.Mixins;
|
using Avalonia.Controls.Mixins;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
using ReactiveUI.Validation.Helpers;
|
||||||
|
|
||||||
namespace Artemis.UI.Shared.Services.PropertyInput;
|
namespace Artemis.UI.Shared.Services.PropertyInput;
|
||||||
|
|
||||||
@ -15,10 +16,11 @@ namespace Artemis.UI.Shared.Services.PropertyInput;
|
|||||||
/// <typeparam name="T">The type of property this input view model supports</typeparam>
|
/// <typeparam name="T">The type of property this input view model supports</typeparam>
|
||||||
public abstract class PropertyInputViewModel<T> : PropertyInputViewModel
|
public abstract class PropertyInputViewModel<T> : PropertyInputViewModel
|
||||||
{
|
{
|
||||||
[AllowNull]
|
|
||||||
private T _inputValue;
|
|
||||||
private bool _inputDragging;
|
|
||||||
private T _dragStartValue;
|
private T _dragStartValue;
|
||||||
|
private bool _inputDragging;
|
||||||
|
|
||||||
|
[AllowNull] private T _inputValue;
|
||||||
|
|
||||||
private TimeSpan _time;
|
private TimeSpan _time;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -89,7 +91,7 @@ public abstract class PropertyInputViewModel<T> : PropertyInputViewModel
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the input value
|
/// Gets or sets the input value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AllowNull]
|
[MaybeNull]
|
||||||
public T InputValue
|
public T InputValue
|
||||||
{
|
{
|
||||||
get => _inputValue;
|
get => _inputValue;
|
||||||
@ -126,13 +128,6 @@ public abstract class PropertyInputViewModel<T> : PropertyInputViewModel
|
|||||||
ProfileEditorService.ExecuteCommand(new UpdateLayerProperty<T>(LayerProperty, _inputValue, _dragStartValue, _time));
|
ProfileEditorService.ExecuteCommand(new UpdateLayerProperty<T>(LayerProperty, _inputValue, _dragStartValue, _time));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called when the input value has been applied to the layer property
|
|
||||||
/// </summary>
|
|
||||||
protected virtual void OnInputValueApplied()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when the input value has changed
|
/// Called when the input value has changed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -147,23 +142,23 @@ public abstract class PropertyInputViewModel<T> : PropertyInputViewModel
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual T GetDragStartValue()
|
/// <summary>
|
||||||
|
/// Called when dragging starts to get the initial value before dragging begun
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The initial value before dragging begun</returns>
|
||||||
|
protected virtual T? GetDragStartValue()
|
||||||
{
|
{
|
||||||
return InputValue;
|
return InputValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Applies the input value to the layer property
|
/// Applies the input value to the layer property using an <see cref="IProfileEditorCommand" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected void ApplyInputValue()
|
protected virtual void ApplyInputValue()
|
||||||
{
|
{
|
||||||
OnInputValueChanged();
|
|
||||||
LayerProperty.SetCurrentValue(_inputValue, _time);
|
|
||||||
OnInputValueApplied();
|
|
||||||
|
|
||||||
if (InputDragging)
|
if (InputDragging)
|
||||||
ProfileEditorService.ChangeTime(_time);
|
ProfileEditorService.ChangeTime(_time);
|
||||||
else
|
else if (ValidationContext.IsValid)
|
||||||
ProfileEditorService.ExecuteCommand(new UpdateLayerProperty<T>(LayerProperty, _inputValue, _time));
|
ProfileEditorService.ExecuteCommand(new UpdateLayerProperty<T>(LayerProperty, _inputValue, _time));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,23 +181,12 @@ public abstract class PropertyInputViewModel<T> : PropertyInputViewModel
|
|||||||
this.RaisePropertyChanged(nameof(IsEnabled));
|
this.RaisePropertyChanged(nameof(IsEnabled));
|
||||||
OnDataBindingsChanged();
|
OnDataBindingsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LayerPropertyOnUpdated(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
UpdateInputValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDataBindingChange(object? sender, DataBindingEventArgs e)
|
|
||||||
{
|
|
||||||
this.RaisePropertyChanged(nameof(IsEnabled));
|
|
||||||
OnDataBindingsChanged();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// For internal use only, implement <see cref="PropertyInputViewModel" /> instead.
|
/// For internal use only, implement <see cref="PropertyInputViewModel" /> instead.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class PropertyInputViewModel : ActivatableViewModelBase
|
public abstract class PropertyInputViewModel : ReactiveValidationObject, IActivatableViewModel, IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Prevents this type being implemented directly, implement
|
/// Prevents this type being implemented directly, implement
|
||||||
@ -210,4 +194,29 @@ public abstract class PropertyInputViewModel : ActivatableViewModelBase
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
// ReSharper disable once UnusedMember.Global
|
// ReSharper disable once UnusedMember.Global
|
||||||
internal abstract object InternalGuard { get; }
|
internal abstract object InternalGuard { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">
|
||||||
|
/// <see langword="true" /> to release both managed and unmanaged resources;
|
||||||
|
/// <see langword="false" /> to release only unmanaged resources.
|
||||||
|
/// </param>
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Implementation of IActivatableViewModel
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ViewModelActivator Activator { get; } = new();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
<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"
|
||||||
|
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia">
|
||||||
<Design.PreviewWith>
|
<Design.PreviewWith>
|
||||||
<Border Padding="50">
|
<Border Padding="50">
|
||||||
<StackPanel Spacing="5">
|
<StackPanel Spacing="5">
|
||||||
@ -9,12 +10,20 @@
|
|||||||
<TextBox Classes="condensed" Text="Hello Down there" />
|
<TextBox Classes="condensed" Text="Hello Down there" />
|
||||||
<TextBox Watermark="Watermark" />
|
<TextBox Watermark="Watermark" />
|
||||||
<TextBox Classes="condensed" Watermark="Watermark" />
|
<TextBox Classes="condensed" Watermark="Watermark" />
|
||||||
<TextBox Text="Test" IsEnabled="False" />
|
|
||||||
<TextBox Classes="condensed" Text="Test" IsEnabled="False" />
|
<controls:NumberBox Value="1337"></controls:NumberBox>
|
||||||
<TextBox Text="Test Clear" Classes="clearButton" />
|
<controls:NumberBox Classes="condensed" Value="1337"></controls:NumberBox>
|
||||||
<TextBox Classes="condensed clearButton" Text="Test Clear" />
|
|
||||||
<TextBox Text="Test Password" Classes="revealPasswordButton" PasswordChar="*" />
|
<ComboBox SelectedIndex="1">
|
||||||
<TextBox Classes="condensed revealPasswordButton" Text="Test Password" PasswordChar="*" />
|
<ComboBoxItem>Bluasdadseheh</ComboBoxItem>
|
||||||
|
<ComboBoxItem>Bluheheheheh</ComboBoxItem>
|
||||||
|
<ComboBoxItem>Bluhgfdgdsheheh</ComboBoxItem>
|
||||||
|
</ComboBox>
|
||||||
|
<ComboBox SelectedIndex="1" Classes="condensed">
|
||||||
|
<ComboBoxItem>Bluasdadseheh</ComboBoxItem>
|
||||||
|
<ComboBoxItem>Bluheheheheh</ComboBoxItem>
|
||||||
|
<ComboBoxItem>Bluhgfdgdsheheh</ComboBoxItem>
|
||||||
|
</ComboBox>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</Design.PreviewWith>
|
</Design.PreviewWith>
|
||||||
@ -22,7 +31,19 @@
|
|||||||
<!-- Add Styles Here -->
|
<!-- Add Styles Here -->
|
||||||
<Style Selector="TextBox.condensed">
|
<Style Selector="TextBox.condensed">
|
||||||
<Setter Property="Padding" Value="4 2" />
|
<Setter Property="Padding" Value="4 2" />
|
||||||
<Setter Property="FontSize" Value="14"></Setter>
|
<Setter Property="FontSize" Value="14" />
|
||||||
<Setter Property="MinHeight" Value="25" />
|
<Setter Property="MinHeight" Value="25" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="controls|NumberBox.condensed /template/ TextBox#InputBox">
|
||||||
|
<Setter Property="Padding" Value="4 2" />
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
<Setter Property="MinHeight" Value="25" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="ComboBox.condensed">
|
||||||
|
<Setter Property="Padding" Value="4 2" />
|
||||||
|
<Setter Property="FontSize" Value="14" />
|
||||||
|
<Setter Property="Height" Value="25" />
|
||||||
|
</Style>
|
||||||
</Styles>
|
</Styles>
|
||||||
@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
@ -47,24 +47,4 @@
|
|||||||
<Resource Include="Assets\Images\Logo\bow.ico" />
|
<Resource Include="Assets\Images\Logo\bow.ico" />
|
||||||
<Resource Include="Assets\Images\Logo\bow.svg" />
|
<Resource Include="Assets\Images\Logo\bow.svg" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<Compile Update="Screens\Debugger\Tabs\Settings\DebugSettingsView.axaml.cs">
|
|
||||||
<DependentUpon>DebugSettingsView.axaml</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Update="Screens\ProfileEditor\Panels\ProfileElementProperties\ProfileElementPropertiesView.axaml.cs">
|
|
||||||
<DependentUpon>ProfileElementPropertiesView.axaml</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Update="Screens\ProfileEditor\Panels\ProfileElementProperties\Windows\BrushConfigurationWindowView.axaml.cs">
|
|
||||||
<DependentUpon>BrushConfigurationWindowView.axaml</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Update="Screens\Sidebar\ContentDialogs\SidebarCategoryEditView.axaml.cs">
|
|
||||||
<DependentUpon>SidebarCategoryEditView.axaml</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Update="Screens\Sidebar\Dialogs\ProfileConfigurationEditView.axaml.cs">
|
|
||||||
<DependentUpon>ProfileConfigurationEditView.axaml</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Screens\ProfileEditor\Tools\" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
</Project>
|
||||||
@ -14,9 +14,8 @@ public class PropertyTreeMarginConverter : IValueConverter
|
|||||||
{
|
{
|
||||||
if (value is TreeGroupViewModel treeGroupViewModel)
|
if (value is TreeGroupViewModel treeGroupViewModel)
|
||||||
return new Thickness(Length * treeGroupViewModel.GetDepth(), 0, 0, 0);
|
return new Thickness(Length * treeGroupViewModel.GetDepth(), 0, 0, 0);
|
||||||
// TODO
|
if (value is ITreePropertyViewModel treePropertyViewModel)
|
||||||
// if (value is ITreePropertyViewModel treePropertyViewModel)
|
return new Thickness(Length * treePropertyViewModel.GetDepth(), 0, 0, 0);
|
||||||
// return new Thickness(Length * treePropertyViewModel.GetDepth(), 0, 0, 0);
|
|
||||||
|
|
||||||
return new Thickness(0);
|
return new Thickness(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,64 @@
|
|||||||
|
<UserControl x:Class="Artemis.UI.DefaultTypes.DataModel.Display.SKColorDataModelDisplayView"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||||
|
xmlns:display="clr-namespace:Artemis.UI.DefaultTypes.DataModel.Display"
|
||||||
|
xmlns:s="https://github.com/canton7/Stylet"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
d:DesignHeight="450" d:DesignWidth="800"
|
||||||
|
d:DataContext="{d:DesignInstance {x:Type display:SKColorDataModelDisplayViewModel}}">
|
||||||
|
<UserControl.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
<ResourceDictionary Source="pack://application:,,,/Artemis.UI.Shared;component/Resources/ArtemisShared.xaml" />
|
||||||
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
<shared:ColorToStringConverter x:Key="SKColorToStringConverter" />
|
||||||
|
<shared:SKColorToColorConverter x:Key="SKColorToColorConverter" />
|
||||||
|
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
|
||||||
|
</ResourceDictionary>
|
||||||
|
</UserControl.Resources>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<!-- Prefix -->
|
||||||
|
<TextBlock Grid.Column="0"
|
||||||
|
Text="{Binding PropertyDescription.Prefix}"
|
||||||
|
Visibility="{Binding PropertyDescription.Prefix, Converter={StaticResource NullToVisibilityConverter}}"
|
||||||
|
TextAlignment="Right"
|
||||||
|
Margin="0 0 5 0" />
|
||||||
|
|
||||||
|
<!-- Value -->
|
||||||
|
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||||
|
<TextBlock x:Name="HexDisplay"
|
||||||
|
Text="{Binding DisplayValue, Converter={StaticResource SKColorToStringConverter}}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
HorizontalAlignment="Stretch" />
|
||||||
|
<Border Width="{Binding ActualHeight, ElementName=HexDisplay}"
|
||||||
|
Height="{Binding ActualHeight, ElementName=HexDisplay}"
|
||||||
|
CornerRadius="{Binding ActualHeight, ElementName=HexDisplay}"
|
||||||
|
Margin="5 0 0 0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Background="{StaticResource Checkerboard}">
|
||||||
|
<Ellipse Stroke="{DynamicResource NormalBorderBrush}">
|
||||||
|
<Ellipse.Fill>
|
||||||
|
<SolidColorBrush Color="{Binding DisplayValue, Converter={StaticResource SKColorToColorConverter}}" />
|
||||||
|
</Ellipse.Fill>
|
||||||
|
</Ellipse>
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- Affix -->
|
||||||
|
<TextBlock Grid.Column="2"
|
||||||
|
Text="{Binding PropertyDescription.Affix}"
|
||||||
|
Visibility="{Binding PropertyDescription.Affix, Converter={StaticResource NullToVisibilityConverter}}"
|
||||||
|
Margin="5 0 0 0" />
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
using Artemis.UI.Shared.DataModelVisualization;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.UI.DefaultTypes.DataModel.Display;
|
||||||
|
|
||||||
|
public class SKColorDataModelDisplayViewModel : DataModelDisplayViewModel<SKColor>
|
||||||
|
{
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
<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"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.DefaultTypes.PropertyInput.BoolPropertyInputView">
|
||||||
|
TODO
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||||
|
{
|
||||||
|
public partial class BoolPropertyInputView : ReactiveUserControl<BoolPropertyInputViewModel>
|
||||||
|
{
|
||||||
|
public BoolPropertyInputView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
|
using Artemis.UI.Shared.Services.PropertyInput;
|
||||||
|
|
||||||
|
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||||
|
|
||||||
|
public class BoolPropertyInputViewModel : PropertyInputViewModel<bool>
|
||||||
|
{
|
||||||
|
public BoolPropertyInputViewModel(LayerProperty<bool> layerProperty, IProfileEditorService profileEditorService, IPropertyInputService propertyInputService)
|
||||||
|
: base(layerProperty, profileEditorService, propertyInputService)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
<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:layerBrushes="clr-namespace:Artemis.Core.LayerBrushes;assembly=Artemis.Core"
|
||||||
|
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.DefaultTypes.PropertyInput.BrushPropertyInputView">
|
||||||
|
|
||||||
|
<UserControl.Styles>
|
||||||
|
<Style Selector="ComboBox.brush /template/ ContentControl#ContentPresenter">
|
||||||
|
<Setter Property="ContentTemplate">
|
||||||
|
<Setter.Value>
|
||||||
|
<DataTemplate DataType="{x:Type layerBrushes:LayerBrushDescriptor}">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<avalonia:MaterialIcon Kind="{Binding Icon}" Height="20" Width="20" VerticalAlignment="Center" Margin="0 0 5 0"/>
|
||||||
|
<TextBlock Text="{Binding DisplayName}" VerticalAlignment="Center" />
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
</UserControl.Styles>
|
||||||
|
<ComboBox Classes="brush condensed"
|
||||||
|
Width="200"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
Items="{Binding Descriptors}"
|
||||||
|
SelectedItem="{Binding SelectedDescriptor}">
|
||||||
|
<ComboBox.ItemTemplate>
|
||||||
|
<DataTemplate DataType="{x:Type layerBrushes:LayerBrushDescriptor}">
|
||||||
|
<Grid ColumnDefinitions="30,*" RowDefinitions="Auto,Auto">
|
||||||
|
<avalonia:MaterialIcon Grid.Row="0"
|
||||||
|
Grid.RowSpan="2"
|
||||||
|
Kind="{Binding Icon}"
|
||||||
|
Height="20"
|
||||||
|
Width="20"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
HorizontalAlignment="Left" />
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding DisplayName}" TextWrapping="Wrap" MaxWidth="350" />
|
||||||
|
<TextBlock Classes="subtitle" Grid.Row="1" Grid.Column="1" Text="{Binding Description}" TextWrapping="Wrap" MaxWidth="350" />
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ComboBox.ItemTemplate>
|
||||||
|
</ComboBox>
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||||
|
{
|
||||||
|
public partial class BrushPropertyInputView : ReactiveUserControl<BrushPropertyInputViewModel>
|
||||||
|
{
|
||||||
|
public BrushPropertyInputView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,82 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.Core.LayerBrushes;
|
||||||
|
using Artemis.Core.Services;
|
||||||
|
using Artemis.UI.Screens.ProfileEditor.ProfileElementProperties.Tree.Dialogs;
|
||||||
|
using Artemis.UI.Shared.Services.Interfaces;
|
||||||
|
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
|
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
||||||
|
using Artemis.UI.Shared.Services.PropertyInput;
|
||||||
|
using Avalonia.Controls.Mixins;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||||
|
|
||||||
|
public class BrushPropertyInputViewModel : PropertyInputViewModel<LayerBrushReference>
|
||||||
|
{
|
||||||
|
private readonly IPluginManagementService _pluginManagementService;
|
||||||
|
private readonly IProfileEditorService _profileEditorService;
|
||||||
|
private readonly IWindowService _windowService;
|
||||||
|
private ObservableCollection<LayerBrushDescriptor> _descriptors;
|
||||||
|
|
||||||
|
public BrushPropertyInputViewModel(LayerProperty<LayerBrushReference> layerProperty, IPluginManagementService pluginManagementService, IWindowService windowService,
|
||||||
|
IProfileEditorService profileEditorService, IPropertyInputService propertyInputService)
|
||||||
|
: base(layerProperty, profileEditorService, propertyInputService)
|
||||||
|
{
|
||||||
|
_pluginManagementService = pluginManagementService;
|
||||||
|
_windowService = windowService;
|
||||||
|
_profileEditorService = profileEditorService;
|
||||||
|
_descriptors = new ObservableCollection<LayerBrushDescriptor>(pluginManagementService.GetFeaturesOfType<LayerBrushProvider>().SelectMany(l => l.LayerBrushDescriptors));
|
||||||
|
|
||||||
|
this.WhenActivated(d =>
|
||||||
|
{
|
||||||
|
Observable.FromEventPattern<PluginFeatureEventArgs>(x => pluginManagementService.PluginFeatureEnabled += x, x => pluginManagementService.PluginFeatureEnabled -= x)
|
||||||
|
.Subscribe(e => UpdateDescriptorsIfChanged(e.EventArgs))
|
||||||
|
.DisposeWith(d);
|
||||||
|
Observable.FromEventPattern<PluginFeatureEventArgs>(x => pluginManagementService.PluginFeatureDisabled += x, x => pluginManagementService.PluginFeatureDisabled -= x)
|
||||||
|
.Subscribe(e => UpdateDescriptorsIfChanged(e.EventArgs))
|
||||||
|
.DisposeWith(d);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableCollection<LayerBrushDescriptor> Descriptors
|
||||||
|
{
|
||||||
|
get => _descriptors;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _descriptors, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LayerBrushDescriptor? SelectedDescriptor
|
||||||
|
{
|
||||||
|
get => Descriptors.FirstOrDefault(d => d.MatchesLayerBrushReference(InputValue));
|
||||||
|
set => SetBrushByDescriptor(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void ApplyInputValue()
|
||||||
|
{
|
||||||
|
if (LayerProperty.ProfileElement is not Layer layer || SelectedDescriptor == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_profileEditorService.ExecuteCommand(new ChangeLayerBrush(layer, SelectedDescriptor));
|
||||||
|
if (layer.LayerBrush?.Presets != null && layer.LayerBrush.Presets.Any())
|
||||||
|
Dispatcher.UIThread.InvokeAsync(() => _windowService.CreateContentDialog().WithViewModel(out LayerBrushPresetViewModel _, ("layerBrush", layer.LayerBrush)).ShowAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateDescriptorsIfChanged(PluginFeatureEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.PluginFeature is not LayerBrushProvider)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Descriptors = new ObservableCollection<LayerBrushDescriptor>(_pluginManagementService.GetFeaturesOfType<LayerBrushProvider>().SelectMany(l => l.LayerBrushDescriptors));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetBrushByDescriptor(LayerBrushDescriptor? value)
|
||||||
|
{
|
||||||
|
if (value != null)
|
||||||
|
InputValue = new LayerBrushReference(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
<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"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.DefaultTypes.PropertyInput.ColorGradientPropertyInputView">
|
||||||
|
TODO
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||||
|
{
|
||||||
|
public partial class ColorGradientPropertyInputView : ReactiveUserControl<ColorGradientPropertyInputViewModel>
|
||||||
|
{
|
||||||
|
public ColorGradientPropertyInputView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
|
using Artemis.UI.Shared.Services.PropertyInput;
|
||||||
|
|
||||||
|
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||||
|
|
||||||
|
public class ColorGradientPropertyInputViewModel : PropertyInputViewModel<ColorGradient>
|
||||||
|
{
|
||||||
|
public ColorGradientPropertyInputViewModel(LayerProperty<ColorGradient> layerProperty, IProfileEditorService profileEditorService, IPropertyInputService propertyInputService)
|
||||||
|
: base(layerProperty, profileEditorService, propertyInputService)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DialogClosed(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
ApplyInputValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
<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"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.DefaultTypes.PropertyInput.EnumPropertyInputView">
|
||||||
|
TODO
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
using Artemis.UI.Shared.Services.PropertyInput;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||||
|
{
|
||||||
|
public partial class EnumPropertyInputView : ReactiveUserControl<PropertyInputViewModel>
|
||||||
|
{
|
||||||
|
public EnumPropertyInputView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
|
using Artemis.UI.Shared.Services.PropertyInput;
|
||||||
|
|
||||||
|
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||||
|
|
||||||
|
public class EnumPropertyInputViewModel<T> : PropertyInputViewModel<T> where T : Enum
|
||||||
|
{
|
||||||
|
public EnumPropertyInputViewModel(LayerProperty<T> layerProperty, IProfileEditorService profileEditorService, IPropertyInputService propertyInputService)
|
||||||
|
: base(layerProperty, profileEditorService, propertyInputService)
|
||||||
|
{
|
||||||
|
// TODO: Test if WhenActivated works here
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type EnumType => typeof(T);
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
<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:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.DefaultTypes.PropertyInput.FloatPropertyInputView">
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||||
|
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" VerticalAlignment="Center" />
|
||||||
|
<controls:NumberBox Classes="condensed"
|
||||||
|
Width="100"
|
||||||
|
Value="{Binding InputValue}"
|
||||||
|
SmallChange="{Binding LayerProperty.PropertyDescription.InputStepSize}"
|
||||||
|
AcceptsExpression="True"
|
||||||
|
VerticalAlignment="Center"/>
|
||||||
|
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" VerticalAlignment="Center"/>
|
||||||
|
</StackPanel>
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||||
|
|
||||||
|
public class FloatPropertyInputView : ReactiveUserControl<FloatPropertyInputViewModel>
|
||||||
|
{
|
||||||
|
public FloatPropertyInputView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
AddHandler(KeyUpEvent, OnRoutedKeyUp, handledEventsToo: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRoutedKeyUp(object? sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Key == Key.Enter || e.Key == Key.Escape)
|
||||||
|
FocusManager.Instance!.Focus(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
|
using Artemis.UI.Shared.Services.PropertyInput;
|
||||||
|
using ReactiveUI.Validation.Extensions;
|
||||||
|
|
||||||
|
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||||
|
|
||||||
|
public class FloatPropertyInputViewModel : PropertyInputViewModel<float>
|
||||||
|
{
|
||||||
|
public FloatPropertyInputViewModel(LayerProperty<float> layerProperty, IProfileEditorService profileEditorService, IPropertyInputService propertyInputService)
|
||||||
|
: base(layerProperty, profileEditorService, propertyInputService)
|
||||||
|
{
|
||||||
|
if (LayerProperty.PropertyDescription.MinInputValue.IsNumber())
|
||||||
|
this.ValidationRule(vm => vm.InputValue, i => i >= (float) LayerProperty.PropertyDescription.MinInputValue,
|
||||||
|
$"Value must be equal to or greater than {LayerProperty.PropertyDescription.MinInputValue}.");
|
||||||
|
if (LayerProperty.PropertyDescription.MaxInputValue.IsNumber())
|
||||||
|
this.ValidationRule(vm => vm.InputValue, i => i < (float) LayerProperty.PropertyDescription.MaxInputValue,
|
||||||
|
$"Value must be smaller than {LayerProperty.PropertyDescription.MaxInputValue}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
<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"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.DefaultTypes.PropertyInput.FloatRangePropertyInputView">
|
||||||
|
TODO
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||||
|
{
|
||||||
|
public partial class FloatRangePropertyInputView : ReactiveUserControl<FloatRangePropertyInputViewModel>
|
||||||
|
{
|
||||||
|
public FloatRangePropertyInputView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
|
using Artemis.UI.Shared.Services.PropertyInput;
|
||||||
|
using ReactiveUI;
|
||||||
|
using ReactiveUI.Validation.Extensions;
|
||||||
|
|
||||||
|
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||||
|
|
||||||
|
public class FloatRangePropertyInputViewModel : PropertyInputViewModel<FloatRange>
|
||||||
|
{
|
||||||
|
public FloatRangePropertyInputViewModel(LayerProperty<FloatRange> layerProperty, IProfileEditorService profileEditorService, IPropertyInputService propertyInputService)
|
||||||
|
: base(layerProperty, profileEditorService, propertyInputService)
|
||||||
|
{
|
||||||
|
this.ValidationRule(vm => vm.Start, start => start <= End, "Start value must be less than the end value.");
|
||||||
|
this.ValidationRule(vm => vm.End, end => end >= Start, "End value must be greater than the start value.");
|
||||||
|
|
||||||
|
if (LayerProperty.PropertyDescription.MinInputValue.IsNumber())
|
||||||
|
{
|
||||||
|
this.ValidationRule(vm => vm.Start, i => i >= (float) LayerProperty.PropertyDescription.MinInputValue,
|
||||||
|
$"Start value must be equal to or greater than {LayerProperty.PropertyDescription.MinInputValue}.");
|
||||||
|
this.ValidationRule(vm => vm.End, i => i >= (float) LayerProperty.PropertyDescription.MinInputValue,
|
||||||
|
$"End value must be equal to or greater than {LayerProperty.PropertyDescription.MinInputValue}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LayerProperty.PropertyDescription.MaxInputValue.IsNumber())
|
||||||
|
{
|
||||||
|
this.ValidationRule(vm => vm.Start, i => i < (float) LayerProperty.PropertyDescription.MaxInputValue,
|
||||||
|
$"Start value must be smaller than {LayerProperty.PropertyDescription.MaxInputValue}.");
|
||||||
|
this.ValidationRule(vm => vm.End, i => i < (float) LayerProperty.PropertyDescription.MaxInputValue,
|
||||||
|
$"End value must be smaller than {LayerProperty.PropertyDescription.MaxInputValue}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public float Start
|
||||||
|
{
|
||||||
|
get => InputValue?.Start ?? 0;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (InputValue == null)
|
||||||
|
InputValue = new FloatRange(value, value + 1);
|
||||||
|
else
|
||||||
|
InputValue.Start = value;
|
||||||
|
|
||||||
|
this.RaisePropertyChanged(nameof(Start));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public float End
|
||||||
|
{
|
||||||
|
get => InputValue?.End ?? 0;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (InputValue == null)
|
||||||
|
InputValue = new FloatRange(value - 1, value);
|
||||||
|
else
|
||||||
|
InputValue.End = value;
|
||||||
|
|
||||||
|
this.RaisePropertyChanged(nameof(End));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected override void OnInputValueChanged()
|
||||||
|
{
|
||||||
|
this.RaisePropertyChanged(nameof(Start));
|
||||||
|
this.RaisePropertyChanged(nameof(End));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
<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:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.DefaultTypes.PropertyInput.IntPropertyInputView">
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||||
|
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" VerticalAlignment="Center" />
|
||||||
|
<controls:NumberBox Classes="condensed"
|
||||||
|
Width="100"
|
||||||
|
Value="{Binding InputValue}"
|
||||||
|
SmallChange="{Binding LayerProperty.PropertyDescription.InputStepSize}"
|
||||||
|
AcceptsExpression="True"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" VerticalAlignment="Center"/>
|
||||||
|
</StackPanel>
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||||
|
|
||||||
|
public class IntPropertyInputView : ReactiveUserControl<IntPropertyInputViewModel>
|
||||||
|
{
|
||||||
|
public IntPropertyInputView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
AddHandler(KeyUpEvent, OnRoutedKeyUp, handledEventsToo: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRoutedKeyUp(object? sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Key == Key.Enter || e.Key == Key.Escape)
|
||||||
|
FocusManager.Instance!.Focus(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
|
using Artemis.UI.Shared.Services.PropertyInput;
|
||||||
|
using ReactiveUI.Validation.Extensions;
|
||||||
|
|
||||||
|
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||||
|
|
||||||
|
public class IntPropertyInputViewModel : PropertyInputViewModel<int>
|
||||||
|
{
|
||||||
|
public IntPropertyInputViewModel(LayerProperty<int> layerProperty, IProfileEditorService profileEditorService, IPropertyInputService propertyInputService)
|
||||||
|
: base(layerProperty, profileEditorService, propertyInputService)
|
||||||
|
{
|
||||||
|
if (LayerProperty.PropertyDescription.MinInputValue.IsNumber())
|
||||||
|
this.ValidationRule(vm => vm.InputValue, i => i >= (int) LayerProperty.PropertyDescription.MinInputValue,
|
||||||
|
$"Value must be equal to or greater than {LayerProperty.PropertyDescription.MinInputValue}.");
|
||||||
|
if (LayerProperty.PropertyDescription.MaxInputValue.IsNumber())
|
||||||
|
this.ValidationRule(vm => vm.InputValue, i => i < (int) LayerProperty.PropertyDescription.MaxInputValue,
|
||||||
|
$"Value must be smaller than {LayerProperty.PropertyDescription.MaxInputValue}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
<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"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.DefaultTypes.PropertyInput.IntRangePropertyInputView">
|
||||||
|
TODO
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||||
|
{
|
||||||
|
public partial class IntRangePropertyInputView : ReactiveUserControl<IntRangePropertyInputViewModel>
|
||||||
|
{
|
||||||
|
public IntRangePropertyInputView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
|
using Artemis.UI.Shared.Services.PropertyInput;
|
||||||
|
using ReactiveUI;
|
||||||
|
using ReactiveUI.Validation.Extensions;
|
||||||
|
|
||||||
|
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||||
|
|
||||||
|
public class IntRangePropertyInputViewModel : PropertyInputViewModel<IntRange>
|
||||||
|
{
|
||||||
|
public IntRangePropertyInputViewModel(LayerProperty<IntRange> layerProperty, IProfileEditorService profileEditorService, IPropertyInputService propertyInputService)
|
||||||
|
: base(layerProperty, profileEditorService, propertyInputService)
|
||||||
|
{
|
||||||
|
this.ValidationRule(vm => vm.Start, start => start <= End, "Start value must be less than the end value.");
|
||||||
|
this.ValidationRule(vm => vm.End, end => end >= Start, "End value must be greater than the start value.");
|
||||||
|
|
||||||
|
if (LayerProperty.PropertyDescription.MinInputValue.IsNumber())
|
||||||
|
{
|
||||||
|
this.ValidationRule(vm => vm.Start, i => i >= (int) LayerProperty.PropertyDescription.MinInputValue,
|
||||||
|
$"Start value must be equal to or greater than {LayerProperty.PropertyDescription.MinInputValue}.");
|
||||||
|
this.ValidationRule(vm => vm.End, i => i >= (int)LayerProperty.PropertyDescription.MinInputValue,
|
||||||
|
$"End value must be equal to or greater than {LayerProperty.PropertyDescription.MinInputValue}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LayerProperty.PropertyDescription.MaxInputValue.IsNumber())
|
||||||
|
{
|
||||||
|
this.ValidationRule(vm => vm.Start, i => i < (int)LayerProperty.PropertyDescription.MaxInputValue,
|
||||||
|
$"Start value must be smaller than {LayerProperty.PropertyDescription.MaxInputValue}.");
|
||||||
|
this.ValidationRule(vm => vm.End, i => i < (int) LayerProperty.PropertyDescription.MaxInputValue,
|
||||||
|
$"End value must be smaller than {LayerProperty.PropertyDescription.MaxInputValue}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Start
|
||||||
|
{
|
||||||
|
get => InputValue?.Start ?? 0;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (InputValue == null)
|
||||||
|
InputValue = new IntRange(value, value + 1);
|
||||||
|
else
|
||||||
|
InputValue.Start = value;
|
||||||
|
|
||||||
|
this.RaisePropertyChanged(nameof(Start));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int End
|
||||||
|
{
|
||||||
|
get => InputValue?.End ?? 0;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (InputValue == null)
|
||||||
|
InputValue = new IntRange(value - 1, value);
|
||||||
|
else
|
||||||
|
InputValue.End = value;
|
||||||
|
|
||||||
|
this.RaisePropertyChanged(nameof(End));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected override void OnInputValueChanged()
|
||||||
|
{
|
||||||
|
this.RaisePropertyChanged(nameof(Start));
|
||||||
|
this.RaisePropertyChanged(nameof(End));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
<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"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.DefaultTypes.PropertyInput.SKColorPropertyInputView">
|
||||||
|
TODO
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||||
|
{
|
||||||
|
public partial class SKColorPropertyInputView : ReactiveUserControl<SKColorPropertyInputViewModel>
|
||||||
|
{
|
||||||
|
public SKColorPropertyInputView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.UI.Shared;
|
||||||
|
using Artemis.UI.Shared.Services;
|
||||||
|
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
|
using Artemis.UI.Shared.Services.PropertyInput;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||||
|
{
|
||||||
|
public class SKColorPropertyInputViewModel : PropertyInputViewModel<SKColor>
|
||||||
|
{
|
||||||
|
public SKColorPropertyInputViewModel(LayerProperty<SKColor> layerProperty, IProfileEditorService profileEditorService, IPropertyInputService propertyInputService)
|
||||||
|
: base(layerProperty, profileEditorService, propertyInputService)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
<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:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.DefaultTypes.PropertyInput.SKPointPropertyInputView">
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||||
|
<TextBlock Width="25"
|
||||||
|
Text="{Binding LayerProperty.PropertyDescription.InputPrefix}"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<controls:NumberBox Classes="condensed"
|
||||||
|
Value="{Binding X}"
|
||||||
|
SmallChange="{Binding LayerProperty.PropertyDescription.InputStepSize}"
|
||||||
|
ToolTip.Tip="X-coordinate (horizontal)"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<TextBlock VerticalAlignment="Center">,</TextBlock>
|
||||||
|
<controls:NumberBox Classes="condensed"
|
||||||
|
Value="{Binding Y}"
|
||||||
|
SmallChange="{Binding LayerProperty.PropertyDescription.InputStepSize}"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<TextBlock Width="25"
|
||||||
|
Text="{Binding LayerProperty.PropertyDescription.InputAffix}"
|
||||||
|
ToolTip.Tip="Y-coordinate (vertical)" />
|
||||||
|
</StackPanel>
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||||
|
{
|
||||||
|
public partial class SKPointPropertyInputView : ReactiveUserControl<SKPointPropertyInputViewModel>
|
||||||
|
{
|
||||||
|
public SKPointPropertyInputView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
AddHandler(KeyUpEvent, OnRoutedKeyUp, handledEventsToo: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRoutedKeyUp(object? sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Key == Key.Enter || e.Key == Key.Escape)
|
||||||
|
FocusManager.Instance!.Focus(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
|
using Artemis.UI.Shared.Services.PropertyInput;
|
||||||
|
using ReactiveUI;
|
||||||
|
using ReactiveUI.Validation.Extensions;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||||
|
|
||||||
|
public class SKPointPropertyInputViewModel : PropertyInputViewModel<SKPoint>
|
||||||
|
{
|
||||||
|
public SKPointPropertyInputViewModel(LayerProperty<SKPoint> layerProperty, IProfileEditorService profileEditorService, IPropertyInputService propertyInputService)
|
||||||
|
: base(layerProperty, profileEditorService, propertyInputService)
|
||||||
|
{
|
||||||
|
if (LayerProperty.PropertyDescription.MinInputValue.IsNumber())
|
||||||
|
{
|
||||||
|
this.ValidationRule(vm => vm.X, i => i >= (float) 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,
|
||||||
|
$"Y must be equal to or greater than {LayerProperty.PropertyDescription.MinInputValue}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LayerProperty.PropertyDescription.MaxInputValue.IsNumber())
|
||||||
|
{
|
||||||
|
this.ValidationRule(vm => vm.X, i => i <= (float) 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,
|
||||||
|
$"Y must be equal to or smaller than {LayerProperty.PropertyDescription.MaxInputValue}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public float X
|
||||||
|
{
|
||||||
|
get => InputValue.X;
|
||||||
|
set => InputValue = new SKPoint(value, Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float Y
|
||||||
|
{
|
||||||
|
get => InputValue.Y;
|
||||||
|
set => InputValue = new SKPoint(X, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected override void OnInputValueChanged()
|
||||||
|
{
|
||||||
|
this.RaisePropertyChanged(nameof(X));
|
||||||
|
this.RaisePropertyChanged(nameof(Y));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
<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:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.DefaultTypes.PropertyInput.SKSizePropertyInputView">
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||||
|
<TextBlock Width="25"
|
||||||
|
Text="{Binding LayerProperty.PropertyDescription.InputPrefix}"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<controls:NumberBox Classes="condensed"
|
||||||
|
Value="{Binding Height}"
|
||||||
|
SmallChange="{Binding LayerProperty.PropertyDescription.InputStepSize}"
|
||||||
|
ToolTip.Tip="Height"
|
||||||
|
VerticalAlignment="Center"/>
|
||||||
|
<TextBlock VerticalAlignment="Center">,</TextBlock>
|
||||||
|
<controls:NumberBox Classes="condensed"
|
||||||
|
Value="{Binding Width}"
|
||||||
|
SmallChange="{Binding LayerProperty.PropertyDescription.InputStepSize}"
|
||||||
|
VerticalAlignment="Center"/>
|
||||||
|
<TextBlock Width="25"
|
||||||
|
Text="{Binding LayerProperty.PropertyDescription.InputAffix}"
|
||||||
|
ToolTip.Tip="Width"
|
||||||
|
VerticalAlignment="Center"/>
|
||||||
|
</StackPanel>
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||||
|
{
|
||||||
|
public partial class SKSizePropertyInputView : ReactiveUserControl<SKSizePropertyInputViewModel>
|
||||||
|
{
|
||||||
|
public SKSizePropertyInputView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
AddHandler(KeyUpEvent, OnRoutedKeyUp, handledEventsToo: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRoutedKeyUp(object? sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Key == Key.Enter || e.Key == Key.Escape)
|
||||||
|
FocusManager.Instance!.Focus(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
|
using Artemis.UI.Shared.Services.PropertyInput;
|
||||||
|
using ReactiveUI;
|
||||||
|
using ReactiveUI.Validation.Extensions;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
// using PropertyChanged;
|
||||||
|
|
||||||
|
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||||
|
|
||||||
|
public class SKSizePropertyInputViewModel : PropertyInputViewModel<SKSize>
|
||||||
|
{
|
||||||
|
public SKSizePropertyInputViewModel(LayerProperty<SKSize> layerProperty, IProfileEditorService profileEditorService, IPropertyInputService propertyInputService)
|
||||||
|
: base(layerProperty, profileEditorService, propertyInputService)
|
||||||
|
{
|
||||||
|
if (LayerProperty.PropertyDescription.MinInputValue.IsNumber())
|
||||||
|
{
|
||||||
|
this.ValidationRule(vm => vm.Width, i => i >= (float) 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,
|
||||||
|
$"Height must be equal to or greater than {LayerProperty.PropertyDescription.MinInputValue}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LayerProperty.PropertyDescription.MaxInputValue.IsNumber())
|
||||||
|
{
|
||||||
|
this.ValidationRule(vm => vm.Width, i => i <= (float) 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,
|
||||||
|
$"Height must be equal to or smaller than {LayerProperty.PropertyDescription.MaxInputValue}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since SKSize is immutable we need to create properties that replace the SKSize entirely
|
||||||
|
public float Width
|
||||||
|
{
|
||||||
|
get => InputValue.Width;
|
||||||
|
set => InputValue = new SKSize(value, Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float Height
|
||||||
|
{
|
||||||
|
get => InputValue.Height;
|
||||||
|
set => InputValue = new SKSize(Width, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnInputValueChanged()
|
||||||
|
{
|
||||||
|
this.RaisePropertyChanged(nameof(Width));
|
||||||
|
this.RaisePropertyChanged(nameof(Height));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,50 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Reactive;
|
|
||||||
using System.Reactive.Disposables;
|
using System.Reactive.Disposables;
|
||||||
using System.Reactive.Linq;
|
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
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.MenuBar
|
namespace Artemis.UI.Screens.ProfileEditor.MenuBar;
|
||||||
|
|
||||||
|
public class MenuBarViewModel : ActivatableViewModelBase
|
||||||
{
|
{
|
||||||
public class MenuBarViewModel : ActivatableViewModelBase
|
|
||||||
{
|
|
||||||
private readonly INotificationService _notificationService;
|
|
||||||
private ProfileEditorHistory? _history;
|
private ProfileEditorHistory? _history;
|
||||||
private Action? _lastMessage;
|
|
||||||
|
|
||||||
public MenuBarViewModel(IProfileEditorService profileEditorService, INotificationService notificationService)
|
public MenuBarViewModel(IProfileEditorService profileEditorService)
|
||||||
{
|
{
|
||||||
_notificationService = notificationService;
|
|
||||||
this.WhenActivated(d => profileEditorService.History.Subscribe(history => History = history).DisposeWith(d));
|
this.WhenActivated(d => profileEditorService.History.Subscribe(history => History = history).DisposeWith(d));
|
||||||
this.WhenAnyValue(x => x.History)
|
|
||||||
.Select(h => h?.Undo ?? Observable.Never<IProfileEditorCommand?>())
|
|
||||||
.Switch()
|
|
||||||
.Subscribe(DisplayUndo);
|
|
||||||
this.WhenAnyValue(x => x.History)
|
|
||||||
.Select(h => h?.Redo ?? Observable.Never<IProfileEditorCommand?>())
|
|
||||||
.Switch()
|
|
||||||
.Subscribe(DisplayRedo);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DisplayUndo(IProfileEditorCommand? command)
|
|
||||||
{
|
|
||||||
if (command == null || History == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_lastMessage?.Invoke();
|
|
||||||
_lastMessage = _notificationService.CreateNotification().WithMessage($"Undid '{command.DisplayName}'.").HavingButton(b => b.WithText("Redo").WithCommand(History.Redo)).Show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DisplayRedo(IProfileEditorCommand? command)
|
|
||||||
{
|
|
||||||
if (command == null || History == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_lastMessage?.Invoke();
|
|
||||||
_notificationService.CreateNotification().WithMessage($"Redid '{command.DisplayName}'.").HavingButton(b => b.WithText("Undo").WithCommand(History.Undo)).Show(); ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProfileEditorHistory? History
|
public ProfileEditorHistory? History
|
||||||
@ -52,5 +20,4 @@ namespace Artemis.UI.Screens.ProfileEditor.MenuBar
|
|||||||
get => _history;
|
get => _history;
|
||||||
set => this.RaiseAndSetIfChanged(ref _history, value);
|
set => this.RaiseAndSetIfChanged(ref _history, value);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -5,11 +5,16 @@
|
|||||||
xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.ProfileElementProperties"
|
xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.ProfileElementProperties"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.Screens.ProfileEditor.ProfileElementProperties.ProfileElementPropertiesView">
|
x:Class="Artemis.UI.Screens.ProfileEditor.ProfileElementProperties.ProfileElementPropertiesView">
|
||||||
<ItemsControl Items="{Binding PropertyGroupViewModels}">
|
<Grid ColumnDefinitions="*,Auto,*">
|
||||||
|
<ItemsControl Grid.Column="0" Items="{Binding PropertyGroupViewModels}">
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<TreeDataTemplate DataType="{x:Type local:ProfileElementPropertyGroupViewModel}" ItemsSource="{Binding Children}">
|
<TreeDataTemplate DataType="{x:Type local:ProfileElementPropertyGroupViewModel}" ItemsSource="{Binding Children}">
|
||||||
<ContentControl Content="{Binding TreeGroupViewModel}" />
|
<ContentControl Content="{Binding TreeGroupViewModel}" />
|
||||||
</TreeDataTemplate>
|
</TreeDataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
|
|
||||||
|
<GridSplitter Grid.Column="1"></GridSplitter>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@ -21,6 +21,8 @@ public class ProfileElementPropertiesViewModel : ActivatableViewModelBase
|
|||||||
private readonly IProfileEditorService _profileEditorService;
|
private readonly IProfileEditorService _profileEditorService;
|
||||||
private ProfileElementPropertyGroupViewModel? _brushPropertyGroup;
|
private ProfileElementPropertyGroupViewModel? _brushPropertyGroup;
|
||||||
private ObservableAsPropertyHelper<RenderProfileElement?>? _profileElement;
|
private ObservableAsPropertyHelper<RenderProfileElement?>? _profileElement;
|
||||||
|
private readonly Dictionary<RenderProfileElement, List<ProfileElementPropertyGroupViewModel>> _profileElementGroups;
|
||||||
|
private ObservableCollection<ProfileElementPropertyGroupViewModel> _propertyGroupViewModels;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ProfileElementPropertiesViewModel(IProfileEditorService profileEditorService, ILayerPropertyVmFactory layerPropertyVmFactory)
|
public ProfileElementPropertiesViewModel(IProfileEditorService profileEditorService, ILayerPropertyVmFactory layerPropertyVmFactory)
|
||||||
@ -28,129 +30,95 @@ public class ProfileElementPropertiesViewModel : ActivatableViewModelBase
|
|||||||
_profileEditorService = profileEditorService;
|
_profileEditorService = profileEditorService;
|
||||||
_layerPropertyVmFactory = layerPropertyVmFactory;
|
_layerPropertyVmFactory = layerPropertyVmFactory;
|
||||||
PropertyGroupViewModels = new ObservableCollection<ProfileElementPropertyGroupViewModel>();
|
PropertyGroupViewModels = new ObservableCollection<ProfileElementPropertyGroupViewModel>();
|
||||||
|
_profileElementGroups = new Dictionary<RenderProfileElement, List<ProfileElementPropertyGroupViewModel>>();
|
||||||
|
|
||||||
// Subscribe to events of the latest selected profile element - borrowed from https://stackoverflow.com/a/63950940
|
// Subscribe to events of the latest selected profile element - borrowed from https://stackoverflow.com/a/63950940
|
||||||
this.WhenAnyValue(x => x.ProfileElement)
|
this.WhenAnyValue(vm => vm.ProfileElement)
|
||||||
.Select(p => p is Layer l
|
.Select(p => p is Layer l
|
||||||
? Observable.FromEventPattern(x => l.LayerBrushUpdated += x, x => l.LayerBrushUpdated -= x)
|
? Observable.FromEventPattern(x => l.LayerBrushUpdated += x, x => l.LayerBrushUpdated -= x)
|
||||||
: Observable.Never<EventPattern<object>>())
|
: Observable.Never<EventPattern<object>>())
|
||||||
.Switch()
|
.Switch()
|
||||||
.Subscribe(_ => ApplyEffects());
|
.Subscribe(_ => UpdateGroups());
|
||||||
this.WhenAnyValue(x => x.ProfileElement)
|
this.WhenAnyValue(vm => vm.ProfileElement)
|
||||||
.Select(p => p != null
|
.Select(p => p != null
|
||||||
? Observable.FromEventPattern(x => p.LayerEffectsUpdated += x, x => p.LayerEffectsUpdated -= x)
|
? Observable.FromEventPattern(x => p.LayerEffectsUpdated += x, x => p.LayerEffectsUpdated -= x)
|
||||||
: Observable.Never<EventPattern<object>>())
|
: Observable.Never<EventPattern<object>>())
|
||||||
.Switch()
|
.Switch()
|
||||||
.Subscribe(_ => ApplyLayerBrush());
|
.Subscribe(_ => UpdateGroups());
|
||||||
|
|
||||||
// React to service profile element changes as long as the VM is active
|
// React to service profile element changes as long as the VM is active
|
||||||
this.WhenActivated(d =>
|
|
||||||
{
|
this.WhenActivated(d => _profileElement = _profileEditorService.ProfileElement.ToProperty(this, vm => vm.ProfileElement).DisposeWith(d));
|
||||||
_profileElement = _profileEditorService.ProfileElement.ToProperty(this, vm => vm.ProfileElement).DisposeWith(d);
|
this.WhenAnyValue(vm => vm.ProfileElement).Subscribe(_ => UpdateGroups());
|
||||||
_profileEditorService.ProfileElement.Subscribe(p => PopulateProperties(p)).DisposeWith(d);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public RenderProfileElement? ProfileElement => _profileElement?.Value;
|
public RenderProfileElement? ProfileElement => _profileElement?.Value;
|
||||||
public Layer? Layer => _profileElement?.Value as Layer;
|
public Layer? Layer => _profileElement?.Value as Layer;
|
||||||
public ObservableCollection<ProfileElementPropertyGroupViewModel> PropertyGroupViewModels { get; }
|
|
||||||
|
|
||||||
private void PopulateProperties(RenderProfileElement? renderProfileElement)
|
public ObservableCollection<ProfileElementPropertyGroupViewModel> PropertyGroupViewModels
|
||||||
|
{
|
||||||
|
get => _propertyGroupViewModels;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _propertyGroupViewModels, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateGroups()
|
||||||
|
{
|
||||||
|
if (ProfileElement == null)
|
||||||
{
|
{
|
||||||
PropertyGroupViewModels.Clear();
|
PropertyGroupViewModels.Clear();
|
||||||
_brushPropertyGroup = null;
|
|
||||||
|
|
||||||
if (ProfileElement == null)
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_profileElementGroups.TryGetValue(ProfileElement, out List<ProfileElementPropertyGroupViewModel>? viewModels))
|
||||||
|
{
|
||||||
|
viewModels = new List<ProfileElementPropertyGroupViewModel>();
|
||||||
|
_profileElementGroups[ProfileElement] = viewModels;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<LayerPropertyGroup> groups = new();
|
||||||
|
|
||||||
// Add layer root groups
|
|
||||||
if (Layer != null)
|
if (Layer != null)
|
||||||
{
|
{
|
||||||
PropertyGroupViewModels.Add(_layerPropertyVmFactory.ProfileElementPropertyGroupViewModel(Layer.General));
|
// Add default layer groups
|
||||||
PropertyGroupViewModels.Add(_layerPropertyVmFactory.ProfileElementPropertyGroupViewModel(Layer.Transform));
|
groups.Add(Layer.General);
|
||||||
ApplyLayerBrush(false);
|
groups.Add(Layer.Transform);
|
||||||
}
|
// Add brush group
|
||||||
|
|
||||||
ApplyEffects();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplyLayerBrush(bool sortProperties = true)
|
|
||||||
{
|
|
||||||
if (Layer == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
bool hideRenderRelatedProperties = Layer.LayerBrush != null && Layer.LayerBrush.SupportsTransformation;
|
|
||||||
|
|
||||||
Layer.General.ShapeType.IsHidden = hideRenderRelatedProperties;
|
|
||||||
Layer.General.BlendMode.IsHidden = hideRenderRelatedProperties;
|
|
||||||
Layer.Transform.IsHidden = hideRenderRelatedProperties;
|
|
||||||
|
|
||||||
if (_brushPropertyGroup != null)
|
|
||||||
{
|
|
||||||
PropertyGroupViewModels.Remove(_brushPropertyGroup);
|
|
||||||
_brushPropertyGroup = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Layer.LayerBrush?.BaseProperties != null)
|
if (Layer.LayerBrush?.BaseProperties != null)
|
||||||
{
|
groups.Add(Layer.LayerBrush.BaseProperties);
|
||||||
_brushPropertyGroup = _layerPropertyVmFactory.ProfileElementPropertyGroupViewModel(Layer.LayerBrush.BaseProperties);
|
|
||||||
PropertyGroupViewModels.Add(_brushPropertyGroup);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sortProperties)
|
// Add effect groups
|
||||||
SortProperties();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplyEffects(bool sortProperties = true)
|
|
||||||
{
|
|
||||||
if (ProfileElement == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Remove VMs of effects no longer applied on the layer
|
|
||||||
List<ProfileElementPropertyGroupViewModel> toRemove = PropertyGroupViewModels
|
|
||||||
.Where(l => l.LayerPropertyGroup.LayerEffect != null && !ProfileElement.LayerEffects.Contains(l.LayerPropertyGroup.LayerEffect))
|
|
||||||
.ToList();
|
|
||||||
foreach (ProfileElementPropertyGroupViewModel profileElementPropertyGroupViewModel in toRemove)
|
|
||||||
PropertyGroupViewModels.Remove(profileElementPropertyGroupViewModel);
|
|
||||||
|
|
||||||
foreach (BaseLayerEffect layerEffect in ProfileElement.LayerEffects)
|
foreach (BaseLayerEffect layerEffect in ProfileElement.LayerEffects)
|
||||||
{
|
{
|
||||||
if (PropertyGroupViewModels.Any(l => l.LayerPropertyGroup.LayerEffect == layerEffect) || layerEffect.BaseProperties == null)
|
if (layerEffect.BaseProperties != null)
|
||||||
continue;
|
groups.Add(layerEffect.BaseProperties);
|
||||||
|
|
||||||
PropertyGroupViewModels.Add(_layerPropertyVmFactory.ProfileElementPropertyGroupViewModel(layerEffect.BaseProperties));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sortProperties)
|
// Remove redundant VMs
|
||||||
SortProperties();
|
viewModels.RemoveAll(vm => !groups.Contains(vm.LayerPropertyGroup));
|
||||||
}
|
|
||||||
|
|
||||||
private void SortProperties()
|
// Create VMs for missing groups
|
||||||
|
foreach (LayerPropertyGroup group in groups)
|
||||||
{
|
{
|
||||||
|
if (viewModels.All(vm => vm.LayerPropertyGroup != group))
|
||||||
|
viewModels.Add(_layerPropertyVmFactory.ProfileElementPropertyGroupViewModel(group));
|
||||||
|
}
|
||||||
|
|
||||||
// Get all non-effect properties
|
// Get all non-effect properties
|
||||||
List<ProfileElementPropertyGroupViewModel> nonEffectProperties = PropertyGroupViewModels
|
List<ProfileElementPropertyGroupViewModel> nonEffectProperties = viewModels
|
||||||
.Where(l => l.TreeGroupViewModel.GroupType != LayerPropertyGroupType.LayerEffectRoot)
|
.Where(l => l.TreeGroupViewModel.GroupType != LayerPropertyGroupType.LayerEffectRoot)
|
||||||
.ToList();
|
.ToList();
|
||||||
// Order the effects
|
// Order the effects
|
||||||
List<ProfileElementPropertyGroupViewModel> effectProperties = PropertyGroupViewModels
|
List<ProfileElementPropertyGroupViewModel> effectProperties = viewModels
|
||||||
.Where(l => l.TreeGroupViewModel.GroupType == LayerPropertyGroupType.LayerEffectRoot && l.LayerPropertyGroup.LayerEffect != null)
|
.Where(l => l.TreeGroupViewModel.GroupType == LayerPropertyGroupType.LayerEffectRoot && l.LayerPropertyGroup.LayerEffect != null)
|
||||||
.OrderBy(l => l.LayerPropertyGroup.LayerEffect?.Order)
|
.OrderBy(l => l.LayerPropertyGroup.LayerEffect?.Order)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
// Put the non-effect properties in front
|
ObservableCollection<ProfileElementPropertyGroupViewModel> propertyGroupViewModels = new();
|
||||||
for (int index = 0; index < nonEffectProperties.Count; index++)
|
foreach (ProfileElementPropertyGroupViewModel viewModel in nonEffectProperties)
|
||||||
{
|
propertyGroupViewModels.Add(viewModel);
|
||||||
ProfileElementPropertyGroupViewModel layerPropertyGroupViewModel = nonEffectProperties[index];
|
foreach (ProfileElementPropertyGroupViewModel viewModel in effectProperties)
|
||||||
if (PropertyGroupViewModels.IndexOf(layerPropertyGroupViewModel) != index)
|
propertyGroupViewModels.Add(viewModel);
|
||||||
PropertyGroupViewModels.Move(PropertyGroupViewModels.IndexOf(layerPropertyGroupViewModel), index);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put the effect properties after, sorted by their order
|
PropertyGroupViewModels = propertyGroupViewModels;
|
||||||
for (int index = 0; index < effectProperties.Count; index++)
|
|
||||||
{
|
|
||||||
ProfileElementPropertyGroupViewModel layerPropertyGroupViewModel = effectProperties[index];
|
|
||||||
if (PropertyGroupViewModels.IndexOf(layerPropertyGroupViewModel) != index + nonEffectProperties.Count)
|
|
||||||
PropertyGroupViewModels.Move(PropertyGroupViewModels.IndexOf(layerPropertyGroupViewModel), index + nonEffectProperties.Count);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -15,9 +15,9 @@ public class ProfileElementPropertyGroupViewModel : ViewModelBase
|
|||||||
{
|
{
|
||||||
private readonly ILayerPropertyVmFactory _layerPropertyVmFactory;
|
private readonly ILayerPropertyVmFactory _layerPropertyVmFactory;
|
||||||
private readonly IPropertyInputService _propertyInputService;
|
private readonly IPropertyInputService _propertyInputService;
|
||||||
private bool _isVisible;
|
|
||||||
private bool _isExpanded;
|
|
||||||
private bool _hasChildren;
|
private bool _hasChildren;
|
||||||
|
private bool _isExpanded;
|
||||||
|
private bool _isVisible;
|
||||||
|
|
||||||
public ProfileElementPropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, ILayerPropertyVmFactory layerPropertyVmFactory, IPropertyInputService propertyInputService)
|
public ProfileElementPropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, ILayerPropertyVmFactory layerPropertyVmFactory, IPropertyInputService propertyInputService)
|
||||||
{
|
{
|
||||||
@ -27,9 +27,34 @@ public class ProfileElementPropertyGroupViewModel : ViewModelBase
|
|||||||
LayerPropertyGroup = layerPropertyGroup;
|
LayerPropertyGroup = layerPropertyGroup;
|
||||||
TreeGroupViewModel = layerPropertyVmFactory.TreeGroupViewModel(this);
|
TreeGroupViewModel = layerPropertyVmFactory.TreeGroupViewModel(this);
|
||||||
|
|
||||||
|
// TODO: Centralize visibility updating or do it here and dispose
|
||||||
|
_isVisible = !LayerPropertyGroup.IsHidden;
|
||||||
|
|
||||||
PopulateChildren();
|
PopulateChildren();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ObservableCollection<ViewModelBase> Children { get; }
|
||||||
|
public LayerPropertyGroup LayerPropertyGroup { get; }
|
||||||
|
public TreeGroupViewModel TreeGroupViewModel { get; }
|
||||||
|
|
||||||
|
public bool IsVisible
|
||||||
|
{
|
||||||
|
get => _isVisible;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _isVisible, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsExpanded
|
||||||
|
{
|
||||||
|
get => _isExpanded;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _isExpanded, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasChildren
|
||||||
|
{
|
||||||
|
get => _hasChildren;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _hasChildren, value);
|
||||||
|
}
|
||||||
|
|
||||||
private void PopulateChildren()
|
private void PopulateChildren()
|
||||||
{
|
{
|
||||||
// Get all properties and property groups and create VMs for them
|
// Get all properties and property groups and create VMs for them
|
||||||
@ -56,26 +81,4 @@ public class ProfileElementPropertyGroupViewModel : ViewModelBase
|
|||||||
|
|
||||||
HasChildren = Children.Any(i => i is ProfileElementPropertyViewModel {IsVisible: true} || i is ProfileElementPropertyGroupViewModel {IsVisible: true});
|
HasChildren = Children.Any(i => i is ProfileElementPropertyViewModel {IsVisible: true} || i is ProfileElementPropertyGroupViewModel {IsVisible: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObservableCollection<ViewModelBase> Children { get; }
|
|
||||||
public LayerPropertyGroup LayerPropertyGroup { get; }
|
|
||||||
public TreeGroupViewModel TreeGroupViewModel { get; }
|
|
||||||
|
|
||||||
public bool IsVisible
|
|
||||||
{
|
|
||||||
get => _isVisible;
|
|
||||||
set => this.RaiseAndSetIfChanged(ref _isVisible, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsExpanded
|
|
||||||
{
|
|
||||||
get => _isExpanded;
|
|
||||||
set => this.RaiseAndSetIfChanged(ref _isExpanded, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasChildren
|
|
||||||
{
|
|
||||||
get => _hasChildren;
|
|
||||||
set => this.RaiseAndSetIfChanged(ref _hasChildren, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -18,6 +18,9 @@ public class ProfileElementPropertyViewModel : ViewModelBase
|
|||||||
LayerProperty = layerProperty;
|
LayerProperty = layerProperty;
|
||||||
TreePropertyViewModel = propertyVmFactory.TreePropertyViewModel(LayerProperty, this);
|
TreePropertyViewModel = propertyVmFactory.TreePropertyViewModel(LayerProperty, this);
|
||||||
TimelinePropertyViewModel = propertyVmFactory.TimelinePropertyViewModel(LayerProperty, this);
|
TimelinePropertyViewModel = propertyVmFactory.TimelinePropertyViewModel(LayerProperty, this);
|
||||||
|
|
||||||
|
// TODO: Centralize visibility updating or do it here and dispose
|
||||||
|
_isVisible = !LayerProperty.IsHidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ILayerProperty LayerProperty { get; }
|
public ILayerProperty LayerProperty { get; }
|
||||||
|
|||||||
@ -0,0 +1,15 @@
|
|||||||
|
using Artemis.Core.LayerBrushes;
|
||||||
|
using Artemis.UI.Shared;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Screens.ProfileEditor.ProfileElementProperties.Tree.Dialogs
|
||||||
|
{
|
||||||
|
public class LayerBrushPresetViewModel : ContentDialogViewModelBase
|
||||||
|
{
|
||||||
|
public BaseLayerBrush LayerBrush { get; }
|
||||||
|
|
||||||
|
public LayerBrushPresetViewModel(BaseLayerBrush layerBrush)
|
||||||
|
{
|
||||||
|
LayerBrush = layerBrush;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,24 +11,24 @@
|
|||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.Screens.ProfileEditor.ProfileElementProperties.Tree.TreeGroupView">
|
x:Class="Artemis.UI.Screens.ProfileEditor.ProfileElementProperties.Tree.TreeGroupView">
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<converters:PropertyTreeMarginConverter x:Key="PropertyTreeMarginConverter" />
|
<converters:PropertyTreeMarginConverter x:Key="PropertyTreeMarginConverter" Length="20"/>
|
||||||
<sharedConverters:EnumToBooleanConverter x:Key="EnumBoolConverter" />
|
<sharedConverters:EnumToBooleanConverter x:Key="EnumBoolConverter" />
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
<UserControl.Styles>
|
<UserControl.Styles>
|
||||||
<Style Selector="avalonia|MaterialIcon.chevron-collapsed">
|
<Style Selector="avalonia|MaterialIcon.chevron-collapsed">
|
||||||
<Setter Property="RenderTransform" Value="rotate(180deg)" />
|
<Setter Property="RenderTransform" Value="rotate(-90deg)" />
|
||||||
</Style>
|
</Style>
|
||||||
</UserControl.Styles>
|
</UserControl.Styles>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<Border Name="Bd"
|
<Border Name="Bd"
|
||||||
BorderBrush="{DynamicResource MaterialDesignDivider}"
|
BorderBrush="{DynamicResource ButtonBorderBrush}"
|
||||||
BorderThickness="0,0,0,1"
|
BorderThickness="0,0,0,1"
|
||||||
Height="25">
|
Height="29">
|
||||||
<Grid Margin="{Binding Converter={StaticResource PropertyTreeMarginConverter}}" ColumnDefinitions="19,*">
|
<Grid Margin="{Binding Converter={StaticResource PropertyTreeMarginConverter}}" ColumnDefinitions="19,*">
|
||||||
|
|
||||||
<avalonia:MaterialIcon Classes.chevron-collapsed="{Binding !ProfileElementPropertyGroupViewModel.IsExpanded}"
|
<avalonia:MaterialIcon Classes.chevron-collapsed="{Binding !ProfileElementPropertyGroupViewModel.IsExpanded}"
|
||||||
IsVisible="{Binding ProfileElementPropertyGroupViewModel.HasChildren}"
|
IsVisible="{Binding ProfileElementPropertyGroupViewModel.HasChildren}"
|
||||||
Kind="ChevronUp"
|
Kind="ChevronDown"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="5 0"
|
Margin="5 0"
|
||||||
PointerPressed="InputElement_OnPointerPressed"
|
PointerPressed="InputElement_OnPointerPressed"
|
||||||
|
|||||||
@ -2,7 +2,64 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
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:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
|
xmlns:converters="clr-namespace:Artemis.UI.Converters"
|
||||||
|
xmlns:sharedConverters="clr-namespace:Artemis.UI.Shared.Converters;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.ProfileElementProperties.Tree.TreePropertyView">
|
x:Class="Artemis.UI.Screens.ProfileEditor.ProfileElementProperties.Tree.TreePropertyView">
|
||||||
Welcome to Avalonia!
|
<UserControl.Resources>
|
||||||
|
<converters:PropertyTreeMarginConverter x:Key="PropertyTreeMarginConverter" Length="20" />
|
||||||
|
<sharedConverters:EnumToBooleanConverter x:Key="EnumBoolConverter" />
|
||||||
|
</UserControl.Resources>
|
||||||
|
<Border Name="Bd"
|
||||||
|
BorderBrush="{DynamicResource ButtonBorderBrush}"
|
||||||
|
BorderThickness="0,0,0,1"
|
||||||
|
Height="29">
|
||||||
|
<Grid Margin="{Binding Converter={StaticResource PropertyTreeMarginConverter}}" ColumnDefinitions="Auto,*,Auto,Auto,Auto">
|
||||||
|
<ToggleButton Grid.Column="0"
|
||||||
|
Classes="icon-button"
|
||||||
|
ToolTip.Tip="Toggle key-framing"
|
||||||
|
Width="24"
|
||||||
|
Height="24"
|
||||||
|
IsChecked="{Binding KeyframesEnabled}"
|
||||||
|
IsEnabled="{Binding LayerProperty.KeyframesSupported}"
|
||||||
|
VerticalAlignment="Center" Padding="-25">
|
||||||
|
<avalonia:MaterialIcon Kind="Stopwatch" />
|
||||||
|
</ToggleButton>
|
||||||
|
|
||||||
|
<TextBlock Grid.Column="1"
|
||||||
|
Margin="5,0,0,0"
|
||||||
|
Padding="0,0,5,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
TextTrimming="CharacterEllipsis"
|
||||||
|
Text="{Binding LayerProperty.PropertyDescription.Name}"
|
||||||
|
ToolTip.Tip="{Binding LayerProperty.PropertyDescription.Description}"
|
||||||
|
HorizontalAlignment="Left" />
|
||||||
|
|
||||||
|
<ContentControl Grid.Column="2"
|
||||||
|
Margin="5 0"
|
||||||
|
Content="{Binding PropertyInputViewModel}"
|
||||||
|
ToolTip.Tip="{Binding LayerProperty.PropertyDescription.Description}"/>
|
||||||
|
|
||||||
|
<Button Grid.Column="3"
|
||||||
|
Command="{Binding ResetToDefault}"
|
||||||
|
Classes="icon-button"
|
||||||
|
ToolTip.Tip="Reset the property to its default value."
|
||||||
|
Width="24"
|
||||||
|
Height="24">
|
||||||
|
<avalonia:MaterialIcon Kind="BackupRestore" />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<ToggleButton Grid.Column="4" Classes="icon-button"
|
||||||
|
ToolTip.Tip="Change the property's data binding"
|
||||||
|
Width="24"
|
||||||
|
Height="24"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
IsEnabled="{Binding LayerProperty.DataBindingsSupported}"
|
||||||
|
IsChecked="{Binding HasDataBinding, Mode=OneWay}">
|
||||||
|
<avalonia:MaterialIcon Kind="VectorLink" />
|
||||||
|
</ToggleButton>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
</Border>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@ -11,8 +11,6 @@ internal class TreePropertyViewModel<T> : ActivatableViewModelBase, ITreePropert
|
|||||||
LayerProperty = layerProperty;
|
LayerProperty = layerProperty;
|
||||||
ProfileElementPropertyViewModel = profileElementPropertyViewModel;
|
ProfileElementPropertyViewModel = profileElementPropertyViewModel;
|
||||||
PropertyInputViewModel = propertyInputService.CreatePropertyInputViewModel(LayerProperty);
|
PropertyInputViewModel = propertyInputService.CreatePropertyInputViewModel(LayerProperty);
|
||||||
|
|
||||||
// TODO: Update ProfileElementPropertyViewModel visibility on change
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public LayerProperty<T> LayerProperty { get; }
|
public LayerProperty<T> LayerProperty { get; }
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Ninject.Factories;
|
using Artemis.UI.Ninject.Factories;
|
||||||
using Artemis.UI.Shared.Services.Interfaces;
|
using Artemis.UI.Shared.Services.Interfaces;
|
||||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
@ -7,8 +8,8 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
|||||||
{
|
{
|
||||||
public class FolderTreeItemViewModel : TreeItemViewModel
|
public class FolderTreeItemViewModel : TreeItemViewModel
|
||||||
{
|
{
|
||||||
public FolderTreeItemViewModel(TreeItemViewModel? parent, Folder folder, IWindowService windowService, IProfileEditorService profileEditorService,
|
public FolderTreeItemViewModel(TreeItemViewModel? parent, Folder folder, IWindowService windowService, IProfileEditorService profileEditorService, IProfileEditorVmFactory profileEditorVmFactory, IRgbService rgbService)
|
||||||
IProfileEditorVmFactory profileEditorVmFactory) : base(parent, folder, windowService, profileEditorService, profileEditorVmFactory)
|
: base(parent, folder, windowService, profileEditorService, rgbService, profileEditorVmFactory)
|
||||||
{
|
{
|
||||||
Folder = folder;
|
Folder = folder;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Ninject.Factories;
|
using Artemis.UI.Ninject.Factories;
|
||||||
using Artemis.UI.Shared.Services.Interfaces;
|
using Artemis.UI.Shared.Services.Interfaces;
|
||||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
@ -7,8 +8,8 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
|||||||
{
|
{
|
||||||
public class LayerTreeItemViewModel : TreeItemViewModel
|
public class LayerTreeItemViewModel : TreeItemViewModel
|
||||||
{
|
{
|
||||||
public LayerTreeItemViewModel(TreeItemViewModel? parent, Layer layer, IWindowService windowService, IProfileEditorService profileEditorService,
|
public LayerTreeItemViewModel(TreeItemViewModel? parent, Layer layer, IWindowService windowService, IProfileEditorService profileEditorService, IProfileEditorVmFactory profileEditorVmFactory, IRgbService rgbService)
|
||||||
IProfileEditorVmFactory profileEditorVmFactory) : base(parent, layer, windowService, profileEditorService, profileEditorVmFactory)
|
: base(parent, layer, windowService, profileEditorService, rgbService, profileEditorVmFactory)
|
||||||
{
|
{
|
||||||
Layer = layer;
|
Layer = layer;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ using System.Linq;
|
|||||||
using System.Reactive;
|
using System.Reactive;
|
||||||
using System.Reactive.Disposables;
|
using System.Reactive.Disposables;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Ninject.Factories;
|
using Artemis.UI.Ninject.Factories;
|
||||||
using Artemis.UI.Shared.Services.Interfaces;
|
using Artemis.UI.Shared.Services.Interfaces;
|
||||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
@ -16,8 +17,8 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
|||||||
{
|
{
|
||||||
private TreeItemViewModel? _selectedChild;
|
private TreeItemViewModel? _selectedChild;
|
||||||
|
|
||||||
public ProfileTreeViewModel(IWindowService windowService, IProfileEditorService profileEditorService, IProfileEditorVmFactory profileEditorVmFactory)
|
public ProfileTreeViewModel(IWindowService windowService, IProfileEditorService profileEditorService, IProfileEditorVmFactory profileEditorVmFactory, IRgbService rgbService)
|
||||||
: base(null, null, windowService, profileEditorService, profileEditorVmFactory)
|
: base(null, null, windowService, profileEditorService, rgbService, profileEditorVmFactory)
|
||||||
{
|
{
|
||||||
this.WhenActivated(d =>
|
this.WhenActivated(d =>
|
||||||
{
|
{
|
||||||
@ -42,8 +43,6 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
|||||||
if (model?.ProfileElement is RenderProfileElement renderProfileElement)
|
if (model?.ProfileElement is RenderProfileElement renderProfileElement)
|
||||||
profileEditorService.ChangeCurrentProfileElement(renderProfileElement);
|
profileEditorService.ChangeCurrentProfileElement(renderProfileElement);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TreeItemViewModel? SelectedChild
|
public TreeItemViewModel? SelectedChild
|
||||||
|
|||||||
@ -7,6 +7,7 @@ using System.Reactive.Disposables;
|
|||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Ninject.Factories;
|
using Artemis.UI.Ninject.Factories;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
using Artemis.UI.Shared.Services.Interfaces;
|
using Artemis.UI.Shared.Services.Interfaces;
|
||||||
@ -27,7 +28,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
|||||||
private bool _renaming;
|
private bool _renaming;
|
||||||
private string? _renameValue;
|
private string? _renameValue;
|
||||||
|
|
||||||
protected TreeItemViewModel(TreeItemViewModel? parent, ProfileElement? profileElement, IWindowService windowService, IProfileEditorService profileEditorService,
|
protected TreeItemViewModel(TreeItemViewModel? parent, ProfileElement? profileElement, IWindowService windowService, IProfileEditorService profileEditorService, IRgbService rgbService,
|
||||||
IProfileEditorVmFactory profileEditorVmFactory)
|
IProfileEditorVmFactory profileEditorVmFactory)
|
||||||
{
|
{
|
||||||
_windowService = windowService;
|
_windowService = windowService;
|
||||||
@ -40,9 +41,17 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
|||||||
AddLayer = ReactiveCommand.Create(() =>
|
AddLayer = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
if (ProfileElement is Layer targetLayer)
|
if (ProfileElement is Layer targetLayer)
|
||||||
profileEditorService.ExecuteCommand(new AddProfileElement(new Layer(targetLayer.Parent, "New layer"), targetLayer.Parent, targetLayer.Order));
|
{
|
||||||
|
Layer layer = new(targetLayer.Parent, "New layer");
|
||||||
|
layer.AddLeds(rgbService.EnabledDevices.SelectMany(d => d.Leds));
|
||||||
|
profileEditorService.ExecuteCommand(new AddProfileElement(layer, targetLayer.Parent, targetLayer.Order));
|
||||||
|
}
|
||||||
else if (ProfileElement != null)
|
else if (ProfileElement != null)
|
||||||
profileEditorService.ExecuteCommand(new AddProfileElement(new Layer(ProfileElement, "New layer"), ProfileElement, 0));
|
{
|
||||||
|
Layer layer = new(ProfileElement, "New layer");
|
||||||
|
layer.AddLeds(rgbService.EnabledDevices.SelectMany(d => d.Leds));
|
||||||
|
profileEditorService.ExecuteCommand(new AddProfileElement(layer, ProfileElement, 0));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
AddFolder = ReactiveCommand.Create(() =>
|
AddFolder = ReactiveCommand.Create(() =>
|
||||||
|
|||||||
@ -0,0 +1,44 @@
|
|||||||
|
<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:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="23"
|
||||||
|
x:Class="Artemis.UI.Screens.ProfileEditor.StatusBar.StatusBarView">
|
||||||
|
<UserControl.Styles>
|
||||||
|
<Style Selector="Border.status-message-border">
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrush}"></Setter>
|
||||||
|
<Setter Property="BorderThickness" Value="1 0 0 0"></Setter>
|
||||||
|
<Setter Property="Margin" Value="5 2 0 2"></Setter>
|
||||||
|
<Setter Property="Transitions">
|
||||||
|
<Setter.Value>
|
||||||
|
<Transitions>
|
||||||
|
<DoubleTransition Property="Opacity" Duration="0.2"></DoubleTransition>
|
||||||
|
</Transitions>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Border.status-message-border.hidden">
|
||||||
|
<Setter Property="Opacity" Value="0" />
|
||||||
|
</Style>
|
||||||
|
</UserControl.Styles>
|
||||||
|
<Grid ColumnDefinitions="Auto, Auto,*" Height="23" Margin="5 0">
|
||||||
|
<ContentControl Grid.Column="0" Content="{Binding ProfileElement}">
|
||||||
|
<ContentControl.DataTemplates>
|
||||||
|
<DataTemplate DataType="core:Folder">
|
||||||
|
<avalonia:MaterialIcon Kind="Folder" Margin="0 0 5 0" />
|
||||||
|
</DataTemplate>
|
||||||
|
<DataTemplate DataType="core:Layer">
|
||||||
|
<avalonia:MaterialIcon Kind="{Binding LayerBrush.Descriptor.Icon}" Margin="0 0 5 0" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ContentControl.DataTemplates>
|
||||||
|
</ContentControl>
|
||||||
|
|
||||||
|
<TextBlock Grid.Column="1" Text="{Binding ProfileElement.Name, FallbackValue=Select a layer to get started}" VerticalAlignment="Center" />
|
||||||
|
|
||||||
|
<Border Grid.Column="2" Classes="status-message-border" Classes.hidden="{Binding !ShowStatusMessage}">
|
||||||
|
<TextBlock Margin="5 0 0 0" Text="{Binding StatusMessage}" />
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Screens.ProfileEditor.StatusBar
|
||||||
|
{
|
||||||
|
public partial class StatusBarView : ReactiveUserControl<StatusBarViewModel>
|
||||||
|
{
|
||||||
|
public StatusBarView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.UI.Shared;
|
||||||
|
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
|
using Avalonia.Controls.Mixins;
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Screens.ProfileEditor.StatusBar;
|
||||||
|
|
||||||
|
public class StatusBarViewModel : ActivatableViewModelBase
|
||||||
|
{
|
||||||
|
private ProfileEditorHistory? _history;
|
||||||
|
private RenderProfileElement? _profileElement;
|
||||||
|
private string? _statusMessage;
|
||||||
|
private bool _showStatusMessage;
|
||||||
|
|
||||||
|
public StatusBarViewModel(IProfileEditorService profileEditorService)
|
||||||
|
{
|
||||||
|
this.WhenActivated(d =>
|
||||||
|
{
|
||||||
|
profileEditorService.ProfileElement.Subscribe(p => ProfileElement = p).DisposeWith(d);
|
||||||
|
profileEditorService.History.Subscribe(history => History = history).DisposeWith(d);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.WhenAnyValue(vm => vm.History)
|
||||||
|
.Select(h => h?.Undo ?? Observable.Never<IProfileEditorCommand?>())
|
||||||
|
.Switch()
|
||||||
|
.Subscribe(c => StatusMessage = c != null ? $"Undid '{c.DisplayName}'." : "Nothing to undo.");
|
||||||
|
this.WhenAnyValue(vm => vm.History)
|
||||||
|
.Select(h => h?.Redo ?? Observable.Never<IProfileEditorCommand?>())
|
||||||
|
.Switch()
|
||||||
|
.Subscribe(c => StatusMessage = c != null ? $"Redid '{c.DisplayName}'." : "Nothing to redo.");
|
||||||
|
|
||||||
|
this.WhenAnyValue(vm => vm.StatusMessage).Subscribe(_ => ShowStatusMessage = true);
|
||||||
|
this.WhenAnyValue(vm => vm.StatusMessage).Throttle(TimeSpan.FromSeconds(3)).Subscribe(_ => ShowStatusMessage = false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RenderProfileElement? ProfileElement
|
||||||
|
{
|
||||||
|
get => _profileElement;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _profileElement, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProfileEditorHistory? History
|
||||||
|
{
|
||||||
|
get => _history;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _history, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string? StatusMessage
|
||||||
|
{
|
||||||
|
get => _statusMessage;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _statusMessage, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShowStatusMessage
|
||||||
|
{
|
||||||
|
get => _showStatusMessage;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _showStatusMessage, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -36,6 +36,10 @@
|
|||||||
</Style>
|
</Style>
|
||||||
</UserControl.Styles>
|
</UserControl.Styles>
|
||||||
<Grid ColumnDefinitions="4*,Auto,*" Classes="editor-grid">
|
<Grid ColumnDefinitions="4*,Auto,*" Classes="editor-grid">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
<ContentControl Content="{Binding MenuBarViewModel}"></ContentControl>
|
<ContentControl Content="{Binding MenuBarViewModel}"></ContentControl>
|
||||||
<Grid Grid.Column="0" RowDefinitions="3*,Auto,*">
|
<Grid Grid.Column="0" RowDefinitions="3*,Auto,*">
|
||||||
<Border Grid.Row="0" Classes="card" Padding="0" Margin="4 0 4 4" ClipToBounds="True">
|
<Border Grid.Row="0" Classes="card" Padding="0" Margin="4 0 4 4" ClipToBounds="True">
|
||||||
@ -80,5 +84,7 @@
|
|||||||
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">Conditions</TextBlock>
|
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">Conditions</TextBlock>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
<ContentControl Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Content="{Binding StatusBarViewModel}"></ContentControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -4,6 +4,7 @@ using Artemis.Core;
|
|||||||
using Artemis.UI.Screens.ProfileEditor.MenuBar;
|
using Artemis.UI.Screens.ProfileEditor.MenuBar;
|
||||||
using Artemis.UI.Screens.ProfileEditor.ProfileElementProperties;
|
using Artemis.UI.Screens.ProfileEditor.ProfileElementProperties;
|
||||||
using Artemis.UI.Screens.ProfileEditor.ProfileTree;
|
using Artemis.UI.Screens.ProfileEditor.ProfileTree;
|
||||||
|
using Artemis.UI.Screens.ProfileEditor.StatusBar;
|
||||||
using Artemis.UI.Screens.ProfileEditor.VisualEditor;
|
using Artemis.UI.Screens.ProfileEditor.VisualEditor;
|
||||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
@ -24,12 +25,14 @@ namespace Artemis.UI.Screens.ProfileEditor
|
|||||||
ProfileTreeViewModel profileTreeViewModel,
|
ProfileTreeViewModel profileTreeViewModel,
|
||||||
ProfileEditorTitleBarViewModel profileEditorTitleBarViewModel,
|
ProfileEditorTitleBarViewModel profileEditorTitleBarViewModel,
|
||||||
MenuBarViewModel menuBarViewModel,
|
MenuBarViewModel menuBarViewModel,
|
||||||
ProfileElementPropertiesViewModel profileElementPropertiesViewModel)
|
ProfileElementPropertiesViewModel profileElementPropertiesViewModel,
|
||||||
|
StatusBarViewModel statusBarViewModel)
|
||||||
: base(hostScreen, "profile-editor")
|
: base(hostScreen, "profile-editor")
|
||||||
{
|
{
|
||||||
VisualEditorViewModel = visualEditorViewModel;
|
VisualEditorViewModel = visualEditorViewModel;
|
||||||
ProfileTreeViewModel = profileTreeViewModel;
|
ProfileTreeViewModel = profileTreeViewModel;
|
||||||
ProfileElementPropertiesViewModel = profileElementPropertiesViewModel;
|
ProfileElementPropertiesViewModel = profileElementPropertiesViewModel;
|
||||||
|
StatusBarViewModel = statusBarViewModel;
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
TitleBarViewModel = profileEditorTitleBarViewModel;
|
TitleBarViewModel = profileEditorTitleBarViewModel;
|
||||||
@ -44,6 +47,7 @@ namespace Artemis.UI.Screens.ProfileEditor
|
|||||||
public ProfileTreeViewModel ProfileTreeViewModel { get; }
|
public ProfileTreeViewModel ProfileTreeViewModel { get; }
|
||||||
public MenuBarViewModel? MenuBarViewModel { get; }
|
public MenuBarViewModel? MenuBarViewModel { get; }
|
||||||
public ProfileElementPropertiesViewModel ProfileElementPropertiesViewModel { get; }
|
public ProfileElementPropertiesViewModel ProfileElementPropertiesViewModel { get; }
|
||||||
|
public StatusBarViewModel StatusBarViewModel { get; }
|
||||||
|
|
||||||
public ProfileConfiguration? ProfileConfiguration => _profileConfiguration?.Value;
|
public ProfileConfiguration? ProfileConfiguration => _profileConfiguration?.Value;
|
||||||
public ProfileEditorHistory? History => _history?.Value;
|
public ProfileEditorHistory? History => _history?.Value;
|
||||||
|
|||||||
@ -27,6 +27,7 @@ namespace Artemis.UI.Screens.Root
|
|||||||
private readonly IDebugService _debugService;
|
private readonly IDebugService _debugService;
|
||||||
private readonly IClassicDesktopStyleApplicationLifetime _lifeTime;
|
private readonly IClassicDesktopStyleApplicationLifetime _lifeTime;
|
||||||
private readonly ISettingsService _settingsService;
|
private readonly ISettingsService _settingsService;
|
||||||
|
private readonly IRegistrationService _registrationService;
|
||||||
private readonly ISidebarVmFactory _sidebarVmFactory;
|
private readonly ISidebarVmFactory _sidebarVmFactory;
|
||||||
private readonly IWindowService _windowService;
|
private readonly IWindowService _windowService;
|
||||||
private SidebarViewModel? _sidebarViewModel;
|
private SidebarViewModel? _sidebarViewModel;
|
||||||
@ -48,6 +49,7 @@ namespace Artemis.UI.Screens.Root
|
|||||||
|
|
||||||
_coreService = coreService;
|
_coreService = coreService;
|
||||||
_settingsService = settingsService;
|
_settingsService = settingsService;
|
||||||
|
_registrationService = registrationService;
|
||||||
_windowService = windowService;
|
_windowService = windowService;
|
||||||
_debugService = debugService;
|
_debugService = debugService;
|
||||||
_assetLoader = assetLoader;
|
_assetLoader = assetLoader;
|
||||||
@ -176,6 +178,10 @@ namespace Artemis.UI.Screens.Root
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OpenMainWindow()
|
public void OpenMainWindow()
|
||||||
{
|
{
|
||||||
|
_registrationService.RegisterBuiltInDataModelDisplays();
|
||||||
|
_registrationService.RegisterBuiltInDataModelInputs();
|
||||||
|
_registrationService.RegisterBuiltInPropertyEditors();
|
||||||
|
|
||||||
if (_lifeTime.MainWindow == null)
|
if (_lifeTime.MainWindow == null)
|
||||||
{
|
{
|
||||||
SidebarViewModel = _sidebarVmFactory.SidebarViewModel(this);
|
SidebarViewModel = _sidebarVmFactory.SidebarViewModel(this);
|
||||||
|
|||||||
@ -1,15 +1,21 @@
|
|||||||
using Artemis.Core.Services;
|
using Artemis.Core;
|
||||||
|
using Artemis.Core.Services;
|
||||||
|
using Artemis.UI.DefaultTypes.PropertyInput;
|
||||||
using Artemis.UI.Services.Interfaces;
|
using Artemis.UI.Services.Interfaces;
|
||||||
|
using Artemis.UI.Shared.Services.PropertyInput;
|
||||||
|
|
||||||
namespace Artemis.UI.Services
|
namespace Artemis.UI.Services
|
||||||
{
|
{
|
||||||
public class RegistrationService : IRegistrationService
|
public class RegistrationService : IRegistrationService
|
||||||
{
|
{
|
||||||
private readonly IInputService _inputService;
|
private readonly IInputService _inputService;
|
||||||
|
private readonly IPropertyInputService _propertyInputService;
|
||||||
|
private bool _registeredBuiltInPropertyEditors;
|
||||||
|
|
||||||
public RegistrationService(IInputService inputService)
|
public RegistrationService(IInputService inputService, IPropertyInputService propertyInputService)
|
||||||
{
|
{
|
||||||
_inputService = inputService;
|
_inputService = inputService;
|
||||||
|
_propertyInputService = propertyInputService;
|
||||||
}
|
}
|
||||||
public void RegisterBuiltInDataModelDisplays()
|
public void RegisterBuiltInDataModelDisplays()
|
||||||
{
|
{
|
||||||
@ -21,6 +27,22 @@ namespace Artemis.UI.Services
|
|||||||
|
|
||||||
public void RegisterBuiltInPropertyEditors()
|
public void RegisterBuiltInPropertyEditors()
|
||||||
{
|
{
|
||||||
|
if (_registeredBuiltInPropertyEditors)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_propertyInputService.RegisterPropertyInput<BrushPropertyInputViewModel>(Constants.CorePlugin);
|
||||||
|
_propertyInputService.RegisterPropertyInput<ColorGradientPropertyInputViewModel>(Constants.CorePlugin);
|
||||||
|
_propertyInputService.RegisterPropertyInput<FloatPropertyInputViewModel>(Constants.CorePlugin);
|
||||||
|
_propertyInputService.RegisterPropertyInput<IntPropertyInputViewModel>(Constants.CorePlugin);
|
||||||
|
_propertyInputService.RegisterPropertyInput<SKColorPropertyInputViewModel>(Constants.CorePlugin);
|
||||||
|
_propertyInputService.RegisterPropertyInput<SKPointPropertyInputViewModel>(Constants.CorePlugin);
|
||||||
|
_propertyInputService.RegisterPropertyInput<SKSizePropertyInputViewModel>(Constants.CorePlugin);
|
||||||
|
_propertyInputService.RegisterPropertyInput(typeof(EnumPropertyInputViewModel<>), Constants.CorePlugin);
|
||||||
|
_propertyInputService.RegisterPropertyInput<BoolPropertyInputViewModel>(Constants.CorePlugin);
|
||||||
|
_propertyInputService.RegisterPropertyInput<FloatRangePropertyInputViewModel>(Constants.CorePlugin);
|
||||||
|
_propertyInputService.RegisterPropertyInput<IntRangePropertyInputViewModel>(Constants.CorePlugin);
|
||||||
|
|
||||||
|
_registeredBuiltInPropertyEditors = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RegisterControllers()
|
public void RegisterControllers()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user