mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Core - Streamlined public parts of profile creation
UI - Started adjusting the VMs for the layer refactor
This commit is contained in:
parent
1de6fefc2a
commit
c07ea09c9d
@ -61,7 +61,7 @@ namespace Artemis.Core
|
||||
/// <returns></returns>
|
||||
public abstract bool EvaluateObject(object target);
|
||||
|
||||
internal abstract void ApplyToEntity();
|
||||
internal abstract void Save();
|
||||
internal abstract DisplayConditionPartEntity GetEntity();
|
||||
|
||||
#region IDisposable
|
||||
|
||||
@ -105,14 +105,14 @@ namespace Artemis.Core
|
||||
};
|
||||
}
|
||||
|
||||
internal override void ApplyToEntity()
|
||||
internal override void Save()
|
||||
{
|
||||
Entity.BooleanOperator = (int) BooleanOperator;
|
||||
|
||||
Entity.Children.Clear();
|
||||
Entity.Children.AddRange(Children.Select(c => c.GetEntity()));
|
||||
foreach (var child in Children)
|
||||
child.ApplyToEntity();
|
||||
child.Save();
|
||||
}
|
||||
|
||||
internal override DisplayConditionPartEntity GetEntity()
|
||||
|
||||
@ -116,7 +116,7 @@ namespace Artemis.Core
|
||||
CompiledListAccessor = lambda.Compile();
|
||||
}
|
||||
|
||||
internal override void ApplyToEntity()
|
||||
internal override void Save()
|
||||
{
|
||||
// Target list
|
||||
if (ListDataModel != null)
|
||||
@ -132,7 +132,7 @@ namespace Artemis.Core
|
||||
Entity.Children.Clear();
|
||||
Entity.Children.AddRange(Children.Select(c => c.GetEntity()));
|
||||
foreach (var child in Children)
|
||||
child.ApplyToEntity();
|
||||
child.Save();
|
||||
}
|
||||
|
||||
internal override DisplayConditionPartEntity GetEntity()
|
||||
|
||||
@ -190,22 +190,31 @@ namespace Artemis.Core
|
||||
return result;
|
||||
}
|
||||
|
||||
internal override void ApplyToEntity()
|
||||
internal override void Save()
|
||||
{
|
||||
Entity.PredicateType = (int) PredicateType;
|
||||
Entity.ListDataModelGuid = ListDataModel?.PluginInfo?.Guid;
|
||||
Entity.ListPropertyPath = ListPropertyPath;
|
||||
if (ListDataModel != null)
|
||||
{
|
||||
Entity.ListDataModelGuid = ListDataModel.PluginInfo.Guid;
|
||||
Entity.ListPropertyPath = ListPropertyPath;
|
||||
}
|
||||
|
||||
Entity.LeftPropertyPath = LeftPropertyPath;
|
||||
Entity.RightPropertyPath = RightPropertyPath;
|
||||
Entity.RightStaticValue = JsonConvert.SerializeObject(RightStaticValue);
|
||||
|
||||
Entity.OperatorPluginGuid = Operator?.PluginInfo?.Guid;
|
||||
Entity.OperatorType = Operator?.GetType().Name;
|
||||
if (Operator != null)
|
||||
{
|
||||
Entity.OperatorPluginGuid = Operator.PluginInfo.Guid;
|
||||
Entity.OperatorType = Operator.GetType().Name;
|
||||
}
|
||||
}
|
||||
|
||||
internal void Initialize()
|
||||
private void Initialize()
|
||||
{
|
||||
ConditionOperatorStore.ConditionOperatorAdded += ConditionOperatorStoreOnConditionOperatorAdded;
|
||||
ConditionOperatorStore.ConditionOperatorRemoved += ConditionOperatorStoreOnConditionOperatorRemoved;
|
||||
|
||||
// Left side
|
||||
if (Entity.LeftPropertyPath != null && ListContainsInnerPath(Entity.LeftPropertyPath))
|
||||
UpdateLeftSide(Entity.LeftPropertyPath);
|
||||
@ -222,7 +231,7 @@ namespace Artemis.Core
|
||||
if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightPropertyPath != null)
|
||||
{
|
||||
if (ListContainsInnerPath(Entity.RightPropertyPath))
|
||||
UpdateLeftSide(Entity.LeftPropertyPath);
|
||||
UpdateRightSideDynamic(Entity.RightPropertyPath);
|
||||
}
|
||||
// Right side static
|
||||
else if (PredicateType == ProfileRightSideType.Static && Entity.RightStaticValue != null)
|
||||
@ -384,5 +393,37 @@ namespace Artemis.Core
|
||||
Expression.Property
|
||||
);
|
||||
}
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void ConditionOperatorStoreOnConditionOperatorAdded(object sender, ConditionOperatorStoreEvent e)
|
||||
{
|
||||
var conditionOperator = e.Registration.ConditionOperator;
|
||||
if (Entity.OperatorPluginGuid == conditionOperator.PluginInfo.Guid && Entity.OperatorType == conditionOperator.GetType().Name)
|
||||
UpdateOperator(conditionOperator);
|
||||
}
|
||||
|
||||
private void ConditionOperatorStoreOnConditionOperatorRemoved(object sender, ConditionOperatorStoreEvent e)
|
||||
{
|
||||
if (e.Registration.ConditionOperator != Operator)
|
||||
return;
|
||||
Operator = null;
|
||||
CompiledListPredicate = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
ConditionOperatorStore.ConditionOperatorAdded -= ConditionOperatorStoreOnConditionOperatorAdded;
|
||||
ConditionOperatorStore.ConditionOperatorRemoved -= ConditionOperatorStoreOnConditionOperatorRemoved;
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -232,7 +232,7 @@ namespace Artemis.Core
|
||||
return $"[Static] {LeftPropertyPath} {Operator.Description} {RightStaticValue}";
|
||||
}
|
||||
|
||||
internal override void ApplyToEntity()
|
||||
internal override void Save()
|
||||
{
|
||||
Entity.PredicateType = (int) PredicateType;
|
||||
Entity.LeftDataModelGuid = LeftDataModel?.PluginInfo?.Guid;
|
||||
@ -250,6 +250,8 @@ namespace Artemis.Core
|
||||
{
|
||||
DataModelStore.DataModelAdded += DataModelStoreOnDataModelAdded;
|
||||
DataModelStore.DataModelRemoved += DataModelStoreOnDataModelRemoved;
|
||||
ConditionOperatorStore.ConditionOperatorAdded += ConditionOperatorStoreOnConditionOperatorAdded;
|
||||
ConditionOperatorStore.ConditionOperatorRemoved += ConditionOperatorStoreOnConditionOperatorRemoved;
|
||||
|
||||
// Left side
|
||||
if (Entity.LeftDataModelGuid != null)
|
||||
@ -415,19 +417,6 @@ namespace Artemis.Core
|
||||
);
|
||||
}
|
||||
|
||||
private Expression CreateListAccessor(DataModel dataModel, string path, ParameterExpression listParameter)
|
||||
{
|
||||
var listType = dataModel.GetListTypeInPath(path);
|
||||
if (listType == null)
|
||||
throw new ArtemisCoreException($"Cannot create a list accessor at path {path} because the path does not contain a list");
|
||||
|
||||
path = dataModel.GetListInnerPath(path);
|
||||
return path.Split('.').Aggregate<string, Expression>(
|
||||
Expression.Convert(listParameter, listType), // Cast to the appropriate type
|
||||
Expression.Property
|
||||
);
|
||||
}
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void DataModelStoreOnDataModelAdded(object sender, DataModelStoreEvent e)
|
||||
@ -454,6 +443,23 @@ namespace Artemis.Core
|
||||
}
|
||||
}
|
||||
|
||||
private void ConditionOperatorStoreOnConditionOperatorAdded(object sender, ConditionOperatorStoreEvent e)
|
||||
{
|
||||
var conditionOperator = e.Registration.ConditionOperator;
|
||||
if (Entity.OperatorPluginGuid == conditionOperator.PluginInfo.Guid && Entity.OperatorType == conditionOperator.GetType().Name)
|
||||
UpdateOperator(conditionOperator);
|
||||
}
|
||||
|
||||
private void ConditionOperatorStoreOnConditionOperatorRemoved(object sender, ConditionOperatorStoreEvent e)
|
||||
{
|
||||
if (e.Registration.ConditionOperator != Operator)
|
||||
return;
|
||||
|
||||
Operator = null;
|
||||
CompiledStaticPredicate = null;
|
||||
CompiledDynamicPredicate = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -461,6 +467,8 @@ namespace Artemis.Core
|
||||
{
|
||||
DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded;
|
||||
DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved;
|
||||
ConditionOperatorStore.ConditionOperatorAdded -= ConditionOperatorStoreOnConditionOperatorAdded;
|
||||
ConditionOperatorStore.ConditionOperatorRemoved -= ConditionOperatorStoreOnConditionOperatorRemoved;
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
@ -8,24 +8,35 @@ using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a folder in a <see cref="Profile" />
|
||||
/// </summary>
|
||||
public sealed class Folder : RenderProfileElement
|
||||
{
|
||||
private SKBitmap _folderBitmap;
|
||||
|
||||
public Folder(Profile profile, ProfileElement parent, string name)
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="Folder" /> class and adds itself to the child collection of the provided
|
||||
/// <paramref name="parent" />
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent of the folder</param>
|
||||
/// <param name="name">The name of the folder</param>
|
||||
public Folder(ProfileElement parent, string name)
|
||||
{
|
||||
FolderEntity = new FolderEntity();
|
||||
EntityId = Guid.NewGuid();
|
||||
|
||||
Profile = profile;
|
||||
Parent = parent;
|
||||
Parent = parent ?? throw new ArgumentNullException(nameof(parent));
|
||||
Profile = Parent.Profile;
|
||||
Name = name;
|
||||
Enabled = true;
|
||||
DisplayContinuously = true;
|
||||
|
||||
_layerEffects = new List<BaseLayerEffect>();
|
||||
_expandedPropertyGroups = new List<string>();
|
||||
|
||||
ApplyRenderElementDefaults();
|
||||
Parent.AddChild(this);
|
||||
}
|
||||
|
||||
internal Folder(Profile profile, ProfileElement parent, FolderEntity folderEntity)
|
||||
@ -182,22 +193,7 @@ namespace Artemis.Core
|
||||
|
||||
canvas.Restore();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new folder to the bottom of this folder
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <returns></returns>
|
||||
public Folder AddFolder(string name)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("Folder");
|
||||
|
||||
var folder = new Folder(Profile, this, name) {Order = Children.LastOrDefault()?.Order ?? 1};
|
||||
AddChild(folder);
|
||||
return folder;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void AddChild(ProfileElement child, int? order = null)
|
||||
{
|
||||
@ -296,7 +292,7 @@ namespace Artemis.Core
|
||||
|
||||
// Conditions
|
||||
RenderElementEntity.RootDisplayCondition = DisplayConditionGroup?.Entity;
|
||||
DisplayConditionGroup?.ApplyToEntity();
|
||||
DisplayConditionGroup?.Save();
|
||||
|
||||
SaveRenderElement();
|
||||
}
|
||||
|
||||
@ -11,8 +11,7 @@ using SkiaSharp;
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a layer on a profile. To create new layers use the <see cref="RenderElementService" /> by injecting
|
||||
/// <see cref="IRenderElementService" /> into your code
|
||||
/// Represents a layer in a <see cref="Profile" />
|
||||
/// </summary>
|
||||
public sealed class Layer : RenderProfileElement
|
||||
{
|
||||
@ -23,13 +22,19 @@ namespace Artemis.Core
|
||||
private List<ArtemisLed> _leds;
|
||||
private LayerTransformProperties _transform;
|
||||
|
||||
internal Layer(Profile profile, ProfileElement parent, string name)
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="Layer" /> class and adds itself to the child collection of the provided
|
||||
/// <paramref name="parent" />
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent of the layer</param>
|
||||
/// <param name="name">The name of the layer</param>
|
||||
public Layer(ProfileElement parent, string name)
|
||||
{
|
||||
LayerEntity = new LayerEntity();
|
||||
EntityId = Guid.NewGuid();
|
||||
|
||||
Profile = profile;
|
||||
Parent = parent;
|
||||
Parent = parent ?? throw new ArgumentNullException(nameof(parent));
|
||||
Profile = Parent.Profile;
|
||||
Name = name;
|
||||
Enabled = true;
|
||||
DisplayContinuously = true;
|
||||
@ -40,10 +45,10 @@ namespace Artemis.Core
|
||||
_leds = new List<ArtemisLed>();
|
||||
_expandedPropertyGroups = new List<string>();
|
||||
|
||||
InitializeDefaultGroups();
|
||||
|
||||
parent.AddChild(this);
|
||||
Initialize();
|
||||
ApplyRenderElementDefaults();
|
||||
|
||||
Parent.AddChild(this);
|
||||
}
|
||||
|
||||
internal Layer(Profile profile, ProfileElement parent, LayerEntity layerEntity)
|
||||
@ -58,8 +63,7 @@ namespace Artemis.Core
|
||||
_leds = new List<ArtemisLed>();
|
||||
_expandedPropertyGroups = new List<string>();
|
||||
|
||||
InitializeDefaultGroups();
|
||||
|
||||
Initialize();
|
||||
Load();
|
||||
}
|
||||
|
||||
@ -114,6 +118,8 @@ namespace Artemis.Core
|
||||
return $"[Layer] {nameof(Name)}: {Name}, {nameof(Order)}: {Order}";
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
@ -133,8 +139,13 @@ namespace Artemis.Core
|
||||
_transform?.Dispose();
|
||||
}
|
||||
|
||||
private void InitializeDefaultGroups()
|
||||
#endregion
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
LayerBrushStore.LayerBrushAdded += LayerBrushStoreOnLayerBrushAdded;
|
||||
LayerBrushStore.LayerBrushRemoved += LayerBrushStoreOnLayerBrushRemoved;
|
||||
|
||||
// Layers have two hardcoded property groups, instantiate them
|
||||
General.Initialize(this, "General.", Constants.CorePluginInfo);
|
||||
Transform.Initialize(this, "Transform.", Constants.CorePluginInfo);
|
||||
@ -191,7 +202,7 @@ namespace Artemis.Core
|
||||
|
||||
// Conditions
|
||||
RenderElementEntity.RootDisplayCondition = DisplayConditionGroup?.Entity;
|
||||
DisplayConditionGroup?.ApplyToEntity();
|
||||
DisplayConditionGroup?.Save();
|
||||
|
||||
SaveRenderElement();
|
||||
}
|
||||
@ -711,6 +722,27 @@ namespace Artemis.Core
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void LayerBrushStoreOnLayerBrushRemoved(object sender, LayerBrushStoreEvent e)
|
||||
{
|
||||
if (LayerBrush.Descriptor == e.Registration.LayerBrushDescriptor)
|
||||
DeactivateLayerBrush();
|
||||
}
|
||||
|
||||
private void LayerBrushStoreOnLayerBrushAdded(object sender, LayerBrushStoreEvent e)
|
||||
{
|
||||
if (LayerBrush != null)
|
||||
return;
|
||||
|
||||
var current = General.BrushReference.CurrentValue;
|
||||
if (e.Registration.Plugin.PluginInfo.Guid == current.BrushPluginGuid &&
|
||||
e.Registration.LayerBrushDescriptor.LayerBrushType.Name == current.BrushType)
|
||||
ActivateLayerBrush();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
public event EventHandler RenderPropertiesUpdated;
|
||||
|
||||
@ -16,17 +16,19 @@ namespace Artemis.Core
|
||||
ProfileEntity = new ProfileEntity();
|
||||
EntityId = Guid.NewGuid();
|
||||
|
||||
Profile = this;
|
||||
Module = module;
|
||||
Name = name;
|
||||
UndoStack = new Stack<string>();
|
||||
RedoStack = new Stack<string>();
|
||||
|
||||
AddChild(new Folder(this, this, "Root folder"));
|
||||
var _ = new Folder(this, "Root folder");
|
||||
Save();
|
||||
}
|
||||
|
||||
internal Profile(ProfileModule module, ProfileEntity profileEntity)
|
||||
{
|
||||
Profile = this;
|
||||
ProfileEntity = profileEntity;
|
||||
EntityId = profileEntity.Id;
|
||||
|
||||
@ -103,7 +105,9 @@ namespace Artemis.Core
|
||||
// Populate the profile starting at the root, the rest is populated recursively
|
||||
var rootFolder = ProfileEntity.Folders.FirstOrDefault(f => f.ParentId == EntityId);
|
||||
if (rootFolder == null)
|
||||
AddChild(new Folder(this, this, "Root folder"));
|
||||
{
|
||||
var _ = new Folder(this, "Root folder");
|
||||
}
|
||||
else
|
||||
AddChild(new Folder(this, this, rootFolder));
|
||||
}
|
||||
|
||||
@ -110,9 +110,12 @@ namespace Artemis.Core
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException(GetType().Name);
|
||||
|
||||
|
||||
lock (ChildrenList)
|
||||
{
|
||||
if (ChildrenList.Contains(child))
|
||||
return;
|
||||
|
||||
// Add to the end of the list
|
||||
if (order == null)
|
||||
{
|
||||
|
||||
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Artemis.Core.LayerEffects;
|
||||
using Artemis.Core.LayerEffects.Placeholder;
|
||||
using Artemis.Core.Properties;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using Artemis.Storage.Entities.Profile.Abstract;
|
||||
@ -49,7 +50,7 @@ namespace Artemis.Core
|
||||
{
|
||||
Id = layerEffect.EntityId,
|
||||
PluginGuid = layerEffect.PluginInfo.Guid,
|
||||
EffectType = layerEffect.GetType().Name,
|
||||
EffectType = layerEffect.GetEffectTypeName(),
|
||||
Name = layerEffect.Name,
|
||||
Enabled = layerEffect.Enabled,
|
||||
HasBeenRenamed = layerEffect.HasBeenRenamed,
|
||||
@ -268,11 +269,29 @@ namespace Artemis.Core
|
||||
{
|
||||
foreach (var layerEffectEntity in RenderElementEntity.LayerEffects)
|
||||
{
|
||||
if (_layerEffects.Any(e => e.EntityId == layerEffectEntity.Id))
|
||||
// If there is a non-placeholder existing effect, skip this entity
|
||||
var existing = _layerEffects.FirstOrDefault(e => e.EntityId == layerEffectEntity.Id);
|
||||
if (existing != null && !existing.Descriptor.IsPlaceHolder)
|
||||
continue;
|
||||
|
||||
var descriptor = LayerEffectStore.Get(layerEffectEntity.PluginGuid, layerEffectEntity.EffectType)?.LayerEffectDescriptor;
|
||||
descriptor?.CreateInstance(this, layerEffectEntity);
|
||||
if (descriptor != null)
|
||||
{
|
||||
// If a descriptor is found but there is an existing placeholder, remove the placeholder
|
||||
if (existing != null)
|
||||
{
|
||||
_layerEffects.Remove(existing);
|
||||
existing.Dispose();
|
||||
}
|
||||
// Create an instance with the descriptor
|
||||
descriptor.CreateInstance(this, layerEffectEntity);
|
||||
}
|
||||
else if (existing == null)
|
||||
{
|
||||
// If no descriptor was found and there was no existing placeholder, create a placeholder
|
||||
descriptor = PlaceholderLayerEffectDescriptor.Create();
|
||||
descriptor.CreateInstance(this, layerEffectEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -297,9 +316,10 @@ namespace Artemis.Core
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private void LayerEffectStoreOnLayerEffectAdded(object? sender, LayerEffectStoreEvent e)
|
||||
private void LayerEffectStoreOnLayerEffectAdded(object sender, LayerEffectStoreEvent e)
|
||||
{
|
||||
ActivateEffects();
|
||||
if (RenderElementEntity.LayerEffects.Any(ef => ef.PluginGuid == e.Registration.Plugin.PluginInfo.Guid))
|
||||
ActivateEffects();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -148,5 +148,7 @@ namespace Artemis.Core.LayerEffects
|
||||
// Not only is this needed to initialize properties on the layer effects, it also prevents implementing anything
|
||||
// but LayerEffect<T> outside the core
|
||||
internal abstract void Initialize();
|
||||
|
||||
internal virtual string GetEffectTypeName() => GetType().Name;
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Artemis.Core.LayerBrushes;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using Ninject;
|
||||
|
||||
@ -51,6 +50,11 @@ namespace Artemis.Core.LayerEffects
|
||||
/// </summary>
|
||||
internal IKernel Kernel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating if this descriptor is a placeholder for a missing plugin
|
||||
/// </summary>
|
||||
public bool IsPlaceHolder { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of the described effect and applies it to the render element
|
||||
/// </summary>
|
||||
@ -60,6 +64,12 @@ namespace Artemis.Core.LayerEffects
|
||||
if (renderElement.LayerEffects.Any(e => e.EntityId == entity.Id))
|
||||
return;
|
||||
|
||||
if (IsPlaceHolder)
|
||||
{
|
||||
CreatePlaceHolderInstance(renderElement, entity);
|
||||
return;
|
||||
}
|
||||
|
||||
var effect = (BaseLayerEffect) Kernel.Get(LayerEffectType);
|
||||
effect.ProfileElement = renderElement;
|
||||
effect.EntityId = entity.Id;
|
||||
@ -73,5 +83,11 @@ namespace Artemis.Core.LayerEffects
|
||||
|
||||
renderElement.ActivateLayerEffect(effect);
|
||||
}
|
||||
|
||||
private void CreatePlaceHolderInstance(RenderProfileElement renderElement, LayerEffectEntity entity)
|
||||
{
|
||||
var effect = new PlaceholderLayerEffect(entity) {ProfileElement = renderElement, Descriptor = this};
|
||||
renderElement.ActivateLayerEffect(effect);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core.LayerEffects
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a layer effect that could not be loaded due to a missing plugin
|
||||
/// </summary>
|
||||
public class PlaceholderLayerEffect : BaseLayerEffect
|
||||
{
|
||||
internal PlaceholderLayerEffect(LayerEffectEntity originalEntity)
|
||||
{
|
||||
OriginalEntity = originalEntity;
|
||||
|
||||
EntityId = OriginalEntity.Id;
|
||||
Order = OriginalEntity.Order;
|
||||
Name = OriginalEntity.Name;
|
||||
Enabled = OriginalEntity.Enabled;
|
||||
HasBeenRenamed = OriginalEntity.HasBeenRenamed;
|
||||
}
|
||||
|
||||
internal LayerEffectEntity OriginalEntity { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void EnableLayerEffect()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void DisableLayerEffect()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(double deltaTime)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void PreProcess(SKCanvas canvas, SKImageInfo canvasInfo, SKPath renderBounds, SKPaint paint)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void PostProcess(SKCanvas canvas, SKImageInfo canvasInfo, SKPath renderBounds, SKPaint paint)
|
||||
{
|
||||
}
|
||||
|
||||
internal override string GetEffectTypeName()
|
||||
{
|
||||
return OriginalEntity.EffectType;
|
||||
}
|
||||
|
||||
internal override void Initialize()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
namespace Artemis.Core.LayerEffects.Placeholder
|
||||
{
|
||||
internal static class PlaceholderLayerEffectDescriptor
|
||||
{
|
||||
public static LayerEffectDescriptor Create()
|
||||
{
|
||||
var descriptor = new LayerEffectDescriptor("Missing effect", "This effect could not be loaded", "FileQuestion", null, null) {IsPlaceHolder = true};
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -51,7 +51,8 @@ namespace Artemis.Core
|
||||
{
|
||||
lock (Registrations)
|
||||
{
|
||||
return Registrations.FirstOrDefault(d => d.Plugin.PluginInfo.Guid == pluginGuid && d.LayerBrushDescriptor.LayerBrushType.Name == typeName);
|
||||
return Registrations.FirstOrDefault(d => d.Plugin.PluginInfo.Guid == pluginGuid &&
|
||||
d.LayerBrushDescriptor.LayerBrushType.Name == typeName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ namespace Artemis.UI.Shared.Services
|
||||
{
|
||||
Profile SelectedProfile { get; }
|
||||
RenderProfileElement SelectedProfileElement { get; }
|
||||
BaseLayerProperty SelectedDataBinding { get; }
|
||||
ILayerProperty SelectedDataBinding { get; }
|
||||
TimeSpan CurrentTime { get; set; }
|
||||
int PixelsPerSecond { get; set; }
|
||||
IReadOnlyList<PropertyInputRegistration> RegisteredPropertyEditors { get; }
|
||||
@ -19,7 +19,7 @@ namespace Artemis.UI.Shared.Services
|
||||
void UpdateSelectedProfile();
|
||||
void ChangeSelectedProfileElement(RenderProfileElement profileElement);
|
||||
void UpdateSelectedProfileElement();
|
||||
void ChangeSelectedDataBinding(BaseLayerProperty layerProperty);
|
||||
void ChangeSelectedDataBinding(ILayerProperty layerProperty);
|
||||
void UpdateProfilePreview();
|
||||
bool UndoUpdateProfile();
|
||||
bool RedoUpdateProfile();
|
||||
@ -88,5 +88,10 @@ namespace Artemis.UI.Shared.Services
|
||||
/// <param name="excludedKeyframe">A keyframe to exclude during keyframe snapping</param>
|
||||
/// <returns></returns>
|
||||
TimeSpan SnapToTimeline(TimeSpan time, TimeSpan tolerance, bool snapToSegments, bool snapToCurrentTime, bool snapToKeyframes, BaseLayerPropertyKeyframe excludedKeyframe = null);
|
||||
|
||||
/// <summary>
|
||||
/// If a matching registration is found, creates a new <see cref="PropertyInputViewModel{T}"/> supporting <typeparamref name="T"/>
|
||||
/// </summary>
|
||||
PropertyInputViewModel<T> CreatePropertyInputViewModel<T>(LayerProperty<T> layerProperty);
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,7 @@ using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.Core.Services;
|
||||
using Ninject;
|
||||
using Ninject.Parameters;
|
||||
using Serilog;
|
||||
using Stylet;
|
||||
|
||||
@ -269,6 +270,16 @@ namespace Artemis.UI.Shared.Services
|
||||
return time;
|
||||
}
|
||||
|
||||
public PropertyInputViewModel<T> CreatePropertyInputViewModel<T>(LayerProperty<T> layerProperty)
|
||||
{
|
||||
var registration = RegisteredPropertyEditors.FirstOrDefault(r => r.SupportedType == typeof(T));
|
||||
if (registration == null)
|
||||
return null;
|
||||
|
||||
var parameter = new ConstructorArgument("layerProperty", layerProperty);
|
||||
return (PropertyInputViewModel<T>) Kernel.Get(registration.ViewModelType, parameter);
|
||||
}
|
||||
|
||||
public ProfileModule GetCurrentModule()
|
||||
{
|
||||
return SelectedProfile?.Module;
|
||||
|
||||
@ -1,13 +1,10 @@
|
||||
using System.Reflection;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.UI.Screens.Modules;
|
||||
using Artemis.UI.Screens.Modules.Tabs;
|
||||
using Artemis.UI.Screens.ProfileEditor;
|
||||
using Artemis.UI.Screens.ProfileEditor.DisplayConditions;
|
||||
using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract;
|
||||
using Artemis.UI.Screens.ProfileEditor.LayerProperties;
|
||||
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Abstract;
|
||||
using Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings;
|
||||
using Artemis.UI.Screens.ProfileEditor.LayerProperties.LayerEffects;
|
||||
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline;
|
||||
@ -19,7 +16,6 @@ using Artemis.UI.Screens.Settings.Debug;
|
||||
using Artemis.UI.Screens.Settings.Tabs.Devices;
|
||||
using Artemis.UI.Screens.Settings.Tabs.Plugins;
|
||||
using Stylet;
|
||||
using Module = Artemis.Core.Modules.Module;
|
||||
|
||||
namespace Artemis.UI.Ninject.Factories
|
||||
{
|
||||
@ -46,15 +42,10 @@ namespace Artemis.UI.Ninject.Factories
|
||||
DeviceDebugViewModel Create(ArtemisDevice device);
|
||||
}
|
||||
|
||||
public interface IFolderVmFactory : IVmFactory
|
||||
public interface IProfileTreeVmFactory : IVmFactory
|
||||
{
|
||||
FolderViewModel Create(ProfileElement folder);
|
||||
FolderViewModel Create(TreeItemViewModel parent, ProfileElement folder);
|
||||
}
|
||||
|
||||
public interface ILayerVmFactory : IVmFactory
|
||||
{
|
||||
LayerViewModel Create(TreeItemViewModel parent, ProfileElement folder);
|
||||
FolderViewModel FolderViewModel(ProfileElement folder);
|
||||
LayerViewModel LayerViewModel(ProfileElement layer);
|
||||
}
|
||||
|
||||
public interface IProfileLayerVmFactory : IVmFactory
|
||||
@ -72,10 +63,10 @@ namespace Artemis.UI.Ninject.Factories
|
||||
|
||||
public interface IDisplayConditionsVmFactory : IVmFactory
|
||||
{
|
||||
DisplayConditionGroupViewModel DisplayConditionGroupViewModel(DisplayConditionGroup displayConditionGroup, DisplayConditionViewModel parent, bool isListGroup);
|
||||
DisplayConditionListViewModel DisplayConditionListViewModel(DisplayConditionList displayConditionList, DisplayConditionViewModel parent);
|
||||
DisplayConditionPredicateViewModel DisplayConditionPredicateViewModel(DisplayConditionPredicate displayConditionPredicate, DisplayConditionViewModel parent);
|
||||
DisplayConditionListPredicateViewModel DisplayConditionListPredicateViewModel(DisplayConditionListPredicate displayConditionListPredicate, DisplayConditionViewModel parent);
|
||||
DisplayConditionGroupViewModel DisplayConditionGroupViewModel(DisplayConditionGroup displayConditionGroup, bool isListGroup);
|
||||
DisplayConditionListViewModel DisplayConditionListViewModel(DisplayConditionList displayConditionList);
|
||||
DisplayConditionPredicateViewModel DisplayConditionPredicateViewModel(DisplayConditionPredicate displayConditionPredicate);
|
||||
DisplayConditionListPredicateViewModel DisplayConditionListPredicateViewModel(DisplayConditionListPredicate displayConditionListPredicate);
|
||||
}
|
||||
|
||||
public interface IDataBindingsVmFactory : IVmFactory
|
||||
@ -87,11 +78,13 @@ namespace Artemis.UI.Ninject.Factories
|
||||
|
||||
public interface ILayerPropertyVmFactory : IVmFactory
|
||||
{
|
||||
LayerPropertyViewModel LayerPropertyViewModel(ILayerProperty layerProperty);
|
||||
LayerPropertyGroupViewModel LayerPropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup);
|
||||
LayerPropertyTreeViewModel<T> LayerPropertyGroupViewModel<T>(LayerProperty<T> layerProperty);
|
||||
LayerPropertyGroupTreeViewModel LayerPropertyGroupTreeViewModel(LayerPropertyGroupViewModel layerPropertyGroupViewModel);
|
||||
LayerPropertyGroupViewModel LayerPropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, PropertyGroupDescriptionAttribute propertyGroupDescription);
|
||||
TreeViewModel TreeViewModel(LayerPropertiesViewModel layerPropertiesViewModel, BindableCollection<LayerPropertyGroupViewModel> layerPropertyGroups);
|
||||
EffectsViewModel EffectsViewModel(LayerPropertiesViewModel layerPropertiesViewModel);
|
||||
TimelineViewModel TimelineViewModel(LayerPropertiesViewModel layerPropertiesViewModel, BindableCollection<LayerPropertyGroupViewModel> layerPropertyGroups);
|
||||
TreePropertyGroupViewModel TreePropertyGroupViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel);
|
||||
TimelinePropertyGroupViewModel TimelinePropertyGroupViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel);
|
||||
}
|
||||
}
|
||||
@ -53,7 +53,7 @@ namespace Artemis.UI.Ninject
|
||||
{
|
||||
x.FromThisAssembly()
|
||||
.SelectAllClasses()
|
||||
.InheritedFrom<ProfileEditorPanelViewModel>()
|
||||
.InheritedFrom<IProfileEditorPanelViewModel>()
|
||||
.BindAllBaseClasses();
|
||||
});
|
||||
|
||||
|
||||
@ -1,44 +1,23 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract
|
||||
{
|
||||
public abstract class DisplayConditionViewModel : PropertyChangedBase, IDisposable
|
||||
public abstract class DisplayConditionViewModel : Conductor<DisplayConditionViewModel>.Collection.AllActive
|
||||
{
|
||||
protected DisplayConditionViewModel(DisplayConditionPart model, DisplayConditionViewModel parent)
|
||||
protected DisplayConditionViewModel(DisplayConditionPart model)
|
||||
{
|
||||
Model = model;
|
||||
Parent = parent;
|
||||
Children = new BindableCollection<DisplayConditionViewModel>();
|
||||
}
|
||||
|
||||
public DisplayConditionPart Model { get; }
|
||||
public DisplayConditionViewModel Parent { get; set; }
|
||||
public BindableCollection<DisplayConditionViewModel> Children { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var child in Children)
|
||||
child.Dispose();
|
||||
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public abstract void Update();
|
||||
|
||||
public virtual void Delete()
|
||||
{
|
||||
Model.Parent.RemoveChild(Model);
|
||||
Parent.Update();
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
}
|
||||
((DisplayConditionViewModel) Parent).Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -124,7 +124,7 @@
|
||||
<materialDesign:PackIcon Kind="Add" Width="18" Height="18" />
|
||||
</Button>
|
||||
|
||||
<ItemsControl Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" ItemsSource="{Binding Children}">
|
||||
<ItemsControl Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" ItemsSource="{Binding Items}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<materialDesign:TransitioningContent>
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract;
|
||||
@ -11,21 +10,24 @@ using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
{
|
||||
public class DisplayConditionGroupViewModel : DisplayConditionViewModel, IViewAware
|
||||
public class DisplayConditionGroupViewModel : DisplayConditionViewModel
|
||||
{
|
||||
private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory;
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private bool _isInitialized;
|
||||
private bool _isRootGroup;
|
||||
|
||||
public DisplayConditionGroupViewModel(DisplayConditionGroup displayConditionGroup, DisplayConditionViewModel parent, bool isListGroup,
|
||||
IProfileEditorService profileEditorService, IDisplayConditionsVmFactory displayConditionsVmFactory) : base(displayConditionGroup, parent)
|
||||
public DisplayConditionGroupViewModel(DisplayConditionGroup displayConditionGroup,
|
||||
bool isListGroup,
|
||||
IProfileEditorService profileEditorService,
|
||||
IDisplayConditionsVmFactory displayConditionsVmFactory)
|
||||
: base(displayConditionGroup)
|
||||
{
|
||||
IsListGroup = isListGroup;
|
||||
_profileEditorService = profileEditorService;
|
||||
_displayConditionsVmFactory = displayConditionsVmFactory;
|
||||
|
||||
Children.CollectionChanged += (sender, args) => NotifyOfPropertyChange(nameof(DisplayBooleanOperator));
|
||||
Items.CollectionChanged += (sender, args) => NotifyOfPropertyChange(nameof(DisplayBooleanOperator));
|
||||
|
||||
Execute.PostToUIThread(async () =>
|
||||
{
|
||||
@ -50,16 +52,9 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
set => SetAndNotify(ref _isInitialized, value);
|
||||
}
|
||||
|
||||
public bool DisplayBooleanOperator => Children.Count > 1;
|
||||
public bool DisplayBooleanOperator => Items.Count > 1;
|
||||
public string SelectedBooleanOperator => DisplayConditionGroup.BooleanOperator.Humanize();
|
||||
|
||||
public void AttachView(UIElement view)
|
||||
{
|
||||
View = view;
|
||||
}
|
||||
|
||||
public UIElement View { get; set; }
|
||||
|
||||
public void SelectBooleanOperator(string type)
|
||||
{
|
||||
var enumValue = Enum.Parse<BooleanOperator>(type);
|
||||
@ -105,39 +100,36 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
NotifyOfPropertyChange(nameof(SelectedBooleanOperator));
|
||||
|
||||
// Remove VMs of effects no longer applied on the layer
|
||||
var toRemove = Children.Where(c => !DisplayConditionGroup.Children.Contains(c.Model)).ToList();
|
||||
var toRemove = Items.Where(c => !DisplayConditionGroup.Children.Contains(c.Model)).ToList();
|
||||
// Using RemoveRange breaks our lovely animations
|
||||
foreach (var displayConditionViewModel in toRemove)
|
||||
{
|
||||
Children.Remove(displayConditionViewModel);
|
||||
displayConditionViewModel.Dispose();
|
||||
}
|
||||
CloseItem(displayConditionViewModel);
|
||||
|
||||
foreach (var childModel in Model.Children)
|
||||
{
|
||||
if (Children.Any(c => c.Model == childModel))
|
||||
if (Items.Any(c => c.Model == childModel))
|
||||
continue;
|
||||
|
||||
switch (childModel)
|
||||
{
|
||||
case DisplayConditionGroup displayConditionGroup:
|
||||
Children.Add(_displayConditionsVmFactory.DisplayConditionGroupViewModel(displayConditionGroup, this, IsListGroup));
|
||||
ActivateItem(_displayConditionsVmFactory.DisplayConditionGroupViewModel(displayConditionGroup, IsListGroup));
|
||||
break;
|
||||
case DisplayConditionList displayConditionListPredicate:
|
||||
Children.Add(_displayConditionsVmFactory.DisplayConditionListViewModel(displayConditionListPredicate, this));
|
||||
ActivateItem(_displayConditionsVmFactory.DisplayConditionListViewModel(displayConditionListPredicate));
|
||||
break;
|
||||
case DisplayConditionPredicate displayConditionPredicate:
|
||||
if (!IsListGroup)
|
||||
Children.Add(_displayConditionsVmFactory.DisplayConditionPredicateViewModel(displayConditionPredicate, this));
|
||||
ActivateItem(_displayConditionsVmFactory.DisplayConditionPredicateViewModel(displayConditionPredicate));
|
||||
break;
|
||||
case DisplayConditionListPredicate displayConditionListPredicate:
|
||||
if (IsListGroup)
|
||||
Children.Add(_displayConditionsVmFactory.DisplayConditionListPredicateViewModel(displayConditionListPredicate, this));
|
||||
ActivateItem(_displayConditionsVmFactory.DisplayConditionListPredicateViewModel(displayConditionListPredicate));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var childViewModel in Children)
|
||||
foreach (var childViewModel in Items)
|
||||
childViewModel.Update();
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,9 +17,9 @@ using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
{
|
||||
public class DisplayConditionListPredicateViewModel : DisplayConditionViewModel, IHandle<MainWindowKeyEvent>, IHandle<MainWindowMouseEvent>
|
||||
public class DisplayConditionListPredicateViewModel : DisplayConditionViewModel, IHandle<MainWindowKeyEvent>, IHandle<MainWindowMouseEvent>, IDisposable
|
||||
{
|
||||
private readonly IDataModelService _dataModelService;
|
||||
private readonly IConditionOperatorService _conditionOperatorService;
|
||||
private readonly IDataModelUIService _dataModelUIService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
@ -39,16 +39,15 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
|
||||
public DisplayConditionListPredicateViewModel(
|
||||
DisplayConditionListPredicate displayConditionListPredicate,
|
||||
DisplayConditionViewModel parent,
|
||||
IProfileEditorService profileEditorService,
|
||||
IDataModelUIService dataModelUIService,
|
||||
IDataModelService dataModelService,
|
||||
IConditionOperatorService conditionOperatorService,
|
||||
ISettingsService settingsService,
|
||||
IEventAggregator eventAggregator) : base(displayConditionListPredicate, parent)
|
||||
IEventAggregator eventAggregator) : base(displayConditionListPredicate)
|
||||
{
|
||||
_profileEditorService = profileEditorService;
|
||||
_dataModelUIService = dataModelUIService;
|
||||
_dataModelService = dataModelService;
|
||||
_conditionOperatorService = conditionOperatorService;
|
||||
_eventAggregator = eventAggregator;
|
||||
_updateTimer = new Timer(500);
|
||||
_supportedInputTypes = new List<Type>();
|
||||
@ -206,7 +205,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
|
||||
// Get the supported operators
|
||||
Operators.Clear();
|
||||
Operators.AddRange(_dataModelService.GetConditionOperatorsForType(leftSideType));
|
||||
Operators.AddRange(_conditionOperatorService.GetConditionOperatorsForType(leftSideType));
|
||||
if (DisplayConditionListPredicate.Operator == null)
|
||||
DisplayConditionListPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType)));
|
||||
SelectedOperator = DisplayConditionListPredicate.Operator;
|
||||
@ -277,12 +276,6 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
_eventAggregator.Subscribe(this);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
_updateTimer.Stop();
|
||||
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
|
||||
}
|
||||
|
||||
private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
if (LeftSideDataModelOpen)
|
||||
@ -353,5 +346,11 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
SelectedOperator = displayConditionOperator;
|
||||
ApplyOperator();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_updateTimer.Dispose();
|
||||
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -105,7 +105,7 @@
|
||||
</Button.ContextMenu>
|
||||
</Button>
|
||||
|
||||
<ItemsControl Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" ItemsSource="{Binding Children}" Margin="0 4 0 0">
|
||||
<ItemsControl Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" ItemsSource="{Binding Items}" Margin="0 4 0 0">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<materialDesign:TransitioningContent>
|
||||
|
||||
@ -14,7 +14,7 @@ using Humanizer;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
{
|
||||
public class DisplayConditionListViewModel : DisplayConditionViewModel
|
||||
public class DisplayConditionListViewModel : DisplayConditionViewModel, IDisposable
|
||||
{
|
||||
private readonly IDataModelUIService _dataModelUIService;
|
||||
private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory;
|
||||
@ -30,7 +30,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
IProfileEditorService profileEditorService,
|
||||
IDataModelUIService dataModelUIService,
|
||||
IDisplayConditionsVmFactory displayConditionsVmFactory,
|
||||
ISettingsService settingsService) : base(displayConditionList, parent)
|
||||
ISettingsService settingsService) : base(displayConditionList)
|
||||
{
|
||||
_profileEditorService = profileEditorService;
|
||||
_dataModelUIService = dataModelUIService;
|
||||
@ -152,36 +152,27 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
TargetDataModel.ApplyTypeFilter(true, typeof(IList));
|
||||
|
||||
// Remove VMs of effects no longer applied on the layer
|
||||
var toRemove = Children.Where(c => !DisplayConditionList.Children.Contains(c.Model)).ToList();
|
||||
var toRemove = Items.Where(c => !DisplayConditionList.Children.Contains(c.Model)).ToList();
|
||||
// Using RemoveRange breaks our lovely animations
|
||||
foreach (var displayConditionViewModel in toRemove)
|
||||
{
|
||||
Children.Remove(displayConditionViewModel);
|
||||
displayConditionViewModel.Dispose();
|
||||
}
|
||||
CloseItem(displayConditionViewModel);
|
||||
|
||||
foreach (var childModel in Model.Children)
|
||||
{
|
||||
if (Children.Any(c => c.Model == childModel))
|
||||
if (Items.Any(c => c.Model == childModel))
|
||||
continue;
|
||||
if (!(childModel is DisplayConditionGroup displayConditionGroup))
|
||||
continue;
|
||||
|
||||
var viewModel = _displayConditionsVmFactory.DisplayConditionGroupViewModel(displayConditionGroup, this, true);
|
||||
var viewModel = _displayConditionsVmFactory.DisplayConditionGroupViewModel(displayConditionGroup, true);
|
||||
viewModel.IsRootGroup = true;
|
||||
Children.Add(viewModel);
|
||||
ActivateItem(viewModel);
|
||||
}
|
||||
|
||||
foreach (var childViewModel in Children)
|
||||
foreach (var childViewModel in Items)
|
||||
childViewModel.Update();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
_updateTimer.Stop();
|
||||
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
|
||||
}
|
||||
|
||||
private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
if (TargetDataModelOpen)
|
||||
@ -205,5 +196,11 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
SelectedListProperty = dataModelListViewModel;
|
||||
ApplyList();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_updateTimer.Dispose();
|
||||
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -16,9 +16,9 @@ using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
{
|
||||
public class DisplayConditionPredicateViewModel : DisplayConditionViewModel, IHandle<MainWindowKeyEvent>, IHandle<MainWindowMouseEvent>
|
||||
public class DisplayConditionPredicateViewModel : DisplayConditionViewModel, IHandle<MainWindowKeyEvent>, IHandle<MainWindowMouseEvent>, IDisposable
|
||||
{
|
||||
private readonly IDataModelService _dataModelService;
|
||||
private readonly IConditionOperatorService _conditionOperatorService;
|
||||
private readonly IDataModelUIService _dataModelUIService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
@ -38,16 +38,15 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
|
||||
public DisplayConditionPredicateViewModel(
|
||||
DisplayConditionPredicate displayConditionPredicate,
|
||||
DisplayConditionViewModel parent,
|
||||
IProfileEditorService profileEditorService,
|
||||
IDataModelUIService dataModelUIService,
|
||||
IDataModelService dataModelService,
|
||||
IConditionOperatorService conditionOperatorService,
|
||||
ISettingsService settingsService,
|
||||
IEventAggregator eventAggregator) : base(displayConditionPredicate, parent)
|
||||
IEventAggregator eventAggregator) : base(displayConditionPredicate)
|
||||
{
|
||||
_profileEditorService = profileEditorService;
|
||||
_dataModelUIService = dataModelUIService;
|
||||
_dataModelService = dataModelService;
|
||||
_conditionOperatorService = conditionOperatorService;
|
||||
_eventAggregator = eventAggregator;
|
||||
_updateTimer = new Timer(500);
|
||||
_supportedInputTypes = new List<Type>();
|
||||
@ -208,7 +207,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
|
||||
// Get the supported operators
|
||||
Operators.Clear();
|
||||
Operators.AddRange(_dataModelService.GetConditionOperatorsForType(leftSideType));
|
||||
Operators.AddRange(_conditionOperatorService.GetConditionOperatorsForType(leftSideType));
|
||||
if (DisplayConditionPredicate.Operator == null)
|
||||
DisplayConditionPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType)));
|
||||
SelectedOperator = DisplayConditionPredicate.Operator;
|
||||
@ -278,13 +277,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
);
|
||||
_eventAggregator.Subscribe(this);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
_updateTimer.Stop();
|
||||
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
|
||||
}
|
||||
|
||||
|
||||
private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
if (LeftSideDataModelOpen)
|
||||
@ -335,5 +328,11 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
SelectedOperator = displayConditionOperator;
|
||||
ApplyOperator();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_updateTimer.Dispose();
|
||||
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -43,7 +43,7 @@
|
||||
|
||||
<Grid Grid.Row="2" Grid.Column="0">
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Background="{StaticResource MaterialDesignCardBackground}">
|
||||
<ContentControl s:View.Model="{Binding RootGroup}" />
|
||||
<ContentControl s:View.Model="{Binding ActiveItem}" />
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
|
||||
|
||||
@ -3,17 +3,17 @@ using Artemis.Core;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
{
|
||||
public class DisplayConditionsViewModel : ProfileEditorPanelViewModel
|
||||
public class DisplayConditionsViewModel : Conductor<DisplayConditionGroupViewModel>, IProfileEditorPanelViewModel
|
||||
{
|
||||
private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory;
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private bool _alwaysFinishTimeline;
|
||||
private bool _displayContinuously;
|
||||
private RenderProfileElement _renderProfileElement;
|
||||
private DisplayConditionGroupViewModel _rootGroup;
|
||||
private int _transitionerIndex;
|
||||
|
||||
public DisplayConditionsViewModel(IProfileEditorService profileEditorService, IDisplayConditionsVmFactory displayConditionsVmFactory)
|
||||
@ -28,11 +28,6 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
set => SetAndNotify(ref _transitionerIndex, value);
|
||||
}
|
||||
|
||||
public DisplayConditionGroupViewModel RootGroup
|
||||
{
|
||||
get => _rootGroup;
|
||||
set => SetAndNotify(ref _rootGroup, value);
|
||||
}
|
||||
|
||||
public RenderProfileElement RenderProfileElement
|
||||
{
|
||||
@ -70,9 +65,6 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
protected override void OnDeactivate()
|
||||
{
|
||||
_profileEditorService.ProfileElementSelected -= ProfileEditorServiceOnProfileElementSelected;
|
||||
|
||||
RootGroup?.Dispose();
|
||||
RootGroup = null;
|
||||
}
|
||||
|
||||
private void ProfileEditorServiceOnProfileElementSelected(object sender, RenderProfileElementEventArgs e)
|
||||
@ -87,8 +79,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
|
||||
if (e.RenderProfileElement == null)
|
||||
{
|
||||
RootGroup?.Dispose();
|
||||
RootGroup = null;
|
||||
ActiveItem = null;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -96,14 +87,13 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
if (e.RenderProfileElement.DisplayConditionGroup == null)
|
||||
e.RenderProfileElement.DisplayConditionGroup = new DisplayConditionGroup(null);
|
||||
|
||||
RootGroup?.Dispose();
|
||||
RootGroup = _displayConditionsVmFactory.DisplayConditionGroupViewModel(e.RenderProfileElement.DisplayConditionGroup, null, false);
|
||||
RootGroup.IsRootGroup = true;
|
||||
RootGroup.Update();
|
||||
ActiveItem = _displayConditionsVmFactory.DisplayConditionGroupViewModel(e.RenderProfileElement.DisplayConditionGroup, false);
|
||||
ActiveItem.IsRootGroup = true;
|
||||
ActiveItem.Update();
|
||||
|
||||
// Only show the intro to conditions once, and only if the layer has no conditions
|
||||
if (TransitionerIndex != 1)
|
||||
TransitionerIndex = RootGroup.Children.Any() ? 1 : 0;
|
||||
TransitionerIndex = ActiveItem.Items.Any() ? 1 : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor
|
||||
{
|
||||
public class ProfileEditorPanelViewModel : Screen
|
||||
public interface IProfileEditorPanelViewModel : IScreen
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Core;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Abstract
|
||||
{
|
||||
public abstract class LayerPropertyBaseViewModel : PropertyChangedBase, IDisposable
|
||||
{
|
||||
private BindableCollection<LayerPropertyBaseViewModel> _children;
|
||||
private bool _isExpanded;
|
||||
|
||||
protected LayerPropertyBaseViewModel()
|
||||
{
|
||||
Children = new BindableCollection<LayerPropertyBaseViewModel>();
|
||||
}
|
||||
|
||||
public abstract bool IsVisible { get; }
|
||||
|
||||
public virtual bool IsExpanded
|
||||
{
|
||||
get => _isExpanded;
|
||||
set => SetAndNotify(ref _isExpanded, value);
|
||||
}
|
||||
|
||||
public BindableCollection<LayerPropertyBaseViewModel> Children
|
||||
{
|
||||
get => _children;
|
||||
set => SetAndNotify(ref _children, value);
|
||||
}
|
||||
|
||||
public abstract void Dispose();
|
||||
|
||||
public abstract List<BaseLayerPropertyKeyframe> GetKeyframes(bool expandedOnly);
|
||||
}
|
||||
}
|
||||
@ -9,32 +9,22 @@ using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.LayerEffects
|
||||
{
|
||||
public class EffectsViewModel : PropertyChangedBase
|
||||
public class EffectsViewModel : Conductor<LayerEffectDescriptor>.Collection.AllActive
|
||||
{
|
||||
private readonly IPluginService _pluginService;
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private readonly IRenderElementService _renderElementService;
|
||||
private BindableCollection<LayerEffectDescriptor> _layerEffectDescriptors;
|
||||
private LayerEffectDescriptor _selectedLayerEffectDescriptor;
|
||||
|
||||
public EffectsViewModel(LayerPropertiesViewModel layerPropertiesViewModel, IPluginService pluginService, IRenderElementService renderElementService, IProfileEditorService profileEditorService)
|
||||
public EffectsViewModel(LayerPropertiesViewModel layerPropertiesViewModel, IPluginService pluginService, IProfileEditorService profileEditorService)
|
||||
{
|
||||
_pluginService = pluginService;
|
||||
_renderElementService = renderElementService;
|
||||
_profileEditorService = profileEditorService;
|
||||
LayerPropertiesViewModel = layerPropertiesViewModel;
|
||||
LayerEffectDescriptors = new BindableCollection<LayerEffectDescriptor>();
|
||||
PropertyChanged += HandleSelectedLayerEffectChanged;
|
||||
}
|
||||
|
||||
public LayerPropertiesViewModel LayerPropertiesViewModel { get; }
|
||||
public bool HasLayerEffectDescriptors => LayerEffectDescriptors.Any();
|
||||
|
||||
public BindableCollection<LayerEffectDescriptor> LayerEffectDescriptors
|
||||
{
|
||||
get => _layerEffectDescriptors;
|
||||
set => SetAndNotify(ref _layerEffectDescriptors, value);
|
||||
}
|
||||
public bool HasLayerEffectDescriptors => Items.Any();
|
||||
|
||||
public LayerEffectDescriptor SelectedLayerEffectDescriptor
|
||||
{
|
||||
@ -46,15 +36,15 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.LayerEffects
|
||||
{
|
||||
var layerBrushProviders = _pluginService.GetPluginsOfType<LayerEffectProvider>();
|
||||
var descriptors = layerBrushProviders.SelectMany(l => l.LayerEffectDescriptors).ToList();
|
||||
LayerEffectDescriptors.AddRange(descriptors.Except(LayerEffectDescriptors));
|
||||
LayerEffectDescriptors.RemoveRange(LayerEffectDescriptors.Except(descriptors));
|
||||
Items.AddRange(descriptors.Except(Items));
|
||||
Items.RemoveRange(Items.Except(descriptors));
|
||||
|
||||
// Sort by display name
|
||||
var index = 0;
|
||||
foreach (var layerEffectDescriptor in LayerEffectDescriptors.OrderBy(d => d.DisplayName).ToList())
|
||||
foreach (var layerEffectDescriptor in Items.OrderBy(d => d.DisplayName).ToList())
|
||||
{
|
||||
if (LayerEffectDescriptors.IndexOf(layerEffectDescriptor) != index)
|
||||
LayerEffectDescriptors.Move(LayerEffectDescriptors.IndexOf(layerEffectDescriptor), index);
|
||||
if (Items.IndexOf(layerEffectDescriptor) != index)
|
||||
((BindableCollection<LayerEffectDescriptor>) Items).Move(Items.IndexOf(layerEffectDescriptor), index);
|
||||
index++;
|
||||
}
|
||||
|
||||
@ -78,7 +68,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.LayerEffects
|
||||
Execute.PostToUIThread(async () =>
|
||||
{
|
||||
await Task.Delay(500);
|
||||
_renderElementService.AddLayerEffect(renderElement, SelectedLayerEffectDescriptor);
|
||||
renderElement.AddLayerEffect(SelectedLayerEffectDescriptor);
|
||||
_profileEditorService.UpdateSelectedProfileElement();
|
||||
});
|
||||
}
|
||||
|
||||
@ -16,11 +16,10 @@ using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using GongSolutions.Wpf.DragDrop;
|
||||
using Stylet;
|
||||
using static Artemis.UI.Screens.ProfileEditor.LayerProperties.LayerPropertyGroupViewModel.ViewModelType;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
|
||||
{
|
||||
public class LayerPropertiesViewModel : ProfileEditorPanelViewModel, IDropTarget
|
||||
public class LayerPropertiesViewModel : Conductor<IScreen>.Collection.AllActive, IProfileEditorPanelViewModel, IDropTarget
|
||||
{
|
||||
private readonly ILayerPropertyVmFactory _layerPropertyVmFactory;
|
||||
private readonly IDataBindingsVmFactory _dataBindingsVmFactory;
|
||||
|
||||
@ -1,58 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Abstract;
|
||||
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline;
|
||||
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Humanizer;
|
||||
using Ninject;
|
||||
using Ninject.Parameters;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
|
||||
{
|
||||
public class LayerPropertyGroupViewModel : LayerPropertyBaseViewModel
|
||||
public class LayerPropertyGroupViewModel : PropertyChangedBase, IDisposable
|
||||
{
|
||||
private readonly ILayerPropertyVmFactory _layerPropertyVmFactory;
|
||||
private ViewModelType _groupType;
|
||||
private TimelinePropertyGroupViewModel _timelinePropertyGroupViewModel;
|
||||
private TreePropertyGroupViewModel _treePropertyGroupViewModel;
|
||||
|
||||
public LayerPropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, PropertyGroupDescriptionAttribute propertyGroupDescription,
|
||||
IProfileEditorService profileEditorService, ILayerPropertyVmFactory layerPropertyVmFactory)
|
||||
public LayerPropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, ILayerPropertyVmFactory layerPropertyVmFactory)
|
||||
{
|
||||
_layerPropertyVmFactory = layerPropertyVmFactory;
|
||||
ProfileEditorService = profileEditorService;
|
||||
|
||||
LayerPropertyGroup = layerPropertyGroup;
|
||||
PropertyGroupDescription = propertyGroupDescription;
|
||||
|
||||
TreePropertyGroupViewModel = _layerPropertyVmFactory.TreePropertyGroupViewModel(this);
|
||||
TimelinePropertyGroupViewModel = _layerPropertyVmFactory.TimelinePropertyGroupViewModel(this);
|
||||
|
||||
// Generate a fallback name if the description does not contain one
|
||||
if (PropertyGroupDescription.Name == null)
|
||||
{
|
||||
var propertyInfo = LayerPropertyGroup.Parent?.GetType().GetProperties().FirstOrDefault(p => ReferenceEquals(p.GetValue(LayerPropertyGroup.Parent), LayerPropertyGroup));
|
||||
if (propertyInfo != null)
|
||||
PropertyGroupDescription.Name = propertyInfo.Name.Humanize();
|
||||
else
|
||||
PropertyGroupDescription.Name = "Unknown group";
|
||||
}
|
||||
|
||||
LayerPropertyGroup.VisibilityChanged += LayerPropertyGroupOnVisibilityChanged;
|
||||
LayerPropertyGroupTreeViewModel = layerPropertyVmFactory.LayerPropertyGroupTreeViewModel(this);
|
||||
PopulateChildren();
|
||||
DetermineType();
|
||||
}
|
||||
|
||||
public override bool IsVisible => !LayerPropertyGroup.IsHidden;
|
||||
public IProfileEditorService ProfileEditorService { get; }
|
||||
public LayerPropertyGroup LayerPropertyGroup { get; }
|
||||
public PropertyGroupDescriptionAttribute PropertyGroupDescription { get; }
|
||||
public LayerPropertyGroupTreeViewModel LayerPropertyGroupTreeViewModel { get; }
|
||||
public BindableCollection<PropertyChangedBase> Children { get; set; }
|
||||
|
||||
public override bool IsExpanded
|
||||
public bool IsVisible => !LayerPropertyGroup.IsHidden;
|
||||
|
||||
public bool IsExpanded
|
||||
{
|
||||
get => LayerPropertyGroup.ProfileElement.IsPropertyGroupExpanded(LayerPropertyGroup);
|
||||
set
|
||||
@ -62,81 +35,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
|
||||
}
|
||||
}
|
||||
|
||||
public ViewModelType GroupType
|
||||
{
|
||||
get => _groupType;
|
||||
set => SetAndNotify(ref _groupType, value);
|
||||
}
|
||||
|
||||
public TreePropertyGroupViewModel TreePropertyGroupViewModel
|
||||
{
|
||||
get => _treePropertyGroupViewModel;
|
||||
set => SetAndNotify(ref _treePropertyGroupViewModel, value);
|
||||
}
|
||||
|
||||
public TimelinePropertyGroupViewModel TimelinePropertyGroupViewModel
|
||||
{
|
||||
get => _timelinePropertyGroupViewModel;
|
||||
set => SetAndNotify(ref _timelinePropertyGroupViewModel, value);
|
||||
}
|
||||
|
||||
public override List<BaseLayerPropertyKeyframe> GetKeyframes(bool expandedOnly)
|
||||
{
|
||||
var result = new List<BaseLayerPropertyKeyframe>();
|
||||
if (expandedOnly && !IsExpanded || LayerPropertyGroup.IsHidden)
|
||||
return result;
|
||||
|
||||
foreach (var layerPropertyBaseViewModel in Children)
|
||||
result.AddRange(layerPropertyBaseViewModel.GetKeyframes(expandedOnly));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
foreach (var layerPropertyBaseViewModel in Children)
|
||||
layerPropertyBaseViewModel.Dispose();
|
||||
|
||||
LayerPropertyGroup.VisibilityChanged -= LayerPropertyGroupOnVisibilityChanged;
|
||||
TimelinePropertyGroupViewModel.Dispose();
|
||||
}
|
||||
|
||||
public List<LayerPropertyBaseViewModel> GetAllChildren()
|
||||
{
|
||||
var result = new List<LayerPropertyBaseViewModel>();
|
||||
foreach (var layerPropertyBaseViewModel in Children)
|
||||
{
|
||||
result.Add(layerPropertyBaseViewModel);
|
||||
if (layerPropertyBaseViewModel is LayerPropertyGroupViewModel layerPropertyGroupViewModel)
|
||||
result.AddRange(layerPropertyGroupViewModel.GetAllChildren());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void UpdateOrder(int order)
|
||||
{
|
||||
LayerPropertyGroup.LayerEffect.Order = order;
|
||||
NotifyOfPropertyChange(nameof(IsExpanded));
|
||||
}
|
||||
|
||||
private void DetermineType()
|
||||
{
|
||||
if (LayerPropertyGroup is LayerGeneralProperties)
|
||||
GroupType = ViewModelType.General;
|
||||
else if (LayerPropertyGroup is LayerTransformProperties)
|
||||
GroupType = ViewModelType.Transform;
|
||||
else if (LayerPropertyGroup.Parent == null && LayerPropertyGroup.LayerBrush != null)
|
||||
GroupType = ViewModelType.LayerBrushRoot;
|
||||
else if (LayerPropertyGroup.Parent == null && LayerPropertyGroup.LayerEffect != null)
|
||||
GroupType = ViewModelType.LayerEffectRoot;
|
||||
else
|
||||
GroupType = ViewModelType.None;
|
||||
}
|
||||
|
||||
private void PopulateChildren()
|
||||
{
|
||||
// Get all properties and property groups and create VMs for them
|
||||
// The group has methods for getting this without reflection but then we lose the order of the properties as they are defined on the group
|
||||
foreach (var propertyInfo in LayerPropertyGroup.GetType().GetProperties())
|
||||
{
|
||||
var propertyAttribute = (PropertyDescriptionAttribute) Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyDescriptionAttribute));
|
||||
@ -144,50 +46,28 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
|
||||
var value = propertyInfo.GetValue(LayerPropertyGroup);
|
||||
|
||||
// Create VMs for properties on the group
|
||||
if (propertyAttribute != null && value is BaseLayerProperty baseLayerProperty)
|
||||
if (propertyAttribute != null && value is ILayerProperty layerProperty)
|
||||
{
|
||||
var viewModel = CreateLayerPropertyViewModel(baseLayerProperty, propertyAttribute);
|
||||
if (viewModel != null)
|
||||
Children.Add(viewModel);
|
||||
var layerPropertyViewModel = _layerPropertyVmFactory.LayerPropertyViewModel(layerProperty);
|
||||
// After creation ensure a supported input VM was found, if not, discard the VM
|
||||
if (!layerPropertyViewModel.LayerPropertyTreeViewModel.HasPropertyInputViewModel)
|
||||
layerPropertyViewModel.Dispose();
|
||||
else
|
||||
Children.Add(layerPropertyViewModel);
|
||||
}
|
||||
// Create VMs for child groups on this group, resulting in a nested structure
|
||||
else if (groupAttribute != null && value is LayerPropertyGroup layerPropertyGroup)
|
||||
Children.Add(_layerPropertyVmFactory.LayerPropertyGroupViewModel(layerPropertyGroup, groupAttribute));
|
||||
Children.Add(_layerPropertyVmFactory.LayerPropertyGroupViewModel(layerPropertyGroup));
|
||||
}
|
||||
}
|
||||
|
||||
private LayerPropertyBaseViewModel CreateLayerPropertyViewModel(BaseLayerProperty baseLayerProperty, PropertyDescriptionAttribute propertyDescription)
|
||||
public void Dispose()
|
||||
{
|
||||
// Go through the pain of instantiating a generic type VM now via reflection to make things a lot simpler down the line
|
||||
var genericType = baseLayerProperty.GetType().Name == typeof(LayerProperty<>).Name
|
||||
? baseLayerProperty.GetType().GetGenericArguments()[0]
|
||||
: baseLayerProperty.GetType().BaseType.GetGenericArguments()[0];
|
||||
|
||||
// Only create entries for types supported by a tree input VM
|
||||
if (!genericType.IsEnum && ProfileEditorService.RegisteredPropertyEditors.All(r => r.SupportedType != genericType))
|
||||
return null;
|
||||
var genericViewModel = typeof(LayerPropertyViewModel<>).MakeGenericType(genericType);
|
||||
var parameters = new IParameter[]
|
||||
foreach (var child in Children)
|
||||
{
|
||||
new ConstructorArgument("layerProperty", baseLayerProperty),
|
||||
new ConstructorArgument("propertyDescription", propertyDescription)
|
||||
};
|
||||
|
||||
return (LayerPropertyBaseViewModel) ProfileEditorService.Kernel.Get(genericViewModel, parameters);
|
||||
}
|
||||
|
||||
private void LayerPropertyGroupOnVisibilityChanged(object sender, EventArgs e)
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(IsVisible));
|
||||
}
|
||||
|
||||
public enum ViewModelType
|
||||
{
|
||||
General,
|
||||
Transform,
|
||||
LayerBrushRoot,
|
||||
LayerEffectRoot,
|
||||
None
|
||||
if (child is IDisposable disposableChild)
|
||||
disposableChild.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,141 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Exceptions;
|
||||
using Artemis.UI.PropertyInput;
|
||||
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Abstract;
|
||||
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline;
|
||||
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Humanizer;
|
||||
using Ninject;
|
||||
using Ninject.Parameters;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
|
||||
{
|
||||
public class LayerPropertyViewModel<T> : LayerPropertyViewModel
|
||||
public class LayerPropertyViewModel : PropertyChangedBase, IDisposable
|
||||
{
|
||||
private TimelinePropertyViewModel<T> _timelinePropertyViewModel;
|
||||
private TreePropertyViewModel<T> _treePropertyViewModel;
|
||||
|
||||
public LayerPropertyViewModel(IProfileEditorService profileEditorService, LayerProperty<T> layerProperty) : base(profileEditorService, layerProperty)
|
||||
public LayerPropertyViewModel(ILayerProperty layerProperty, IKernel kernel)
|
||||
{
|
||||
LayerProperty = layerProperty;
|
||||
|
||||
TreePropertyViewModel = CreateTreePropertyViewModel();
|
||||
TimelinePropertyViewModel = new TimelinePropertyViewModel<T>(this, profileEditorService);
|
||||
var parameter = new ConstructorArgument("layerProperty", LayerProperty);
|
||||
var treeViewModelType = typeof(LayerPropertyTreeViewModel<>).MakeGenericType(layerProperty.GetType().GetGenericArguments());
|
||||
var timelineViewModelType = typeof(LayerPropertyTimelineViewModel<>).MakeGenericType(layerProperty.GetType().GetGenericArguments());
|
||||
|
||||
TreePropertyBaseViewModel = TreePropertyViewModel;
|
||||
TimelinePropertyBaseViewModel = TimelinePropertyViewModel;
|
||||
|
||||
// Generate a fallback name if the description does not contain one
|
||||
if (LayerProperty.PropertyDescription.Name == null)
|
||||
{
|
||||
var propertyInfo = LayerProperty.Parent?.GetType().GetProperties().FirstOrDefault(p => ReferenceEquals(p.GetValue(LayerProperty.Parent), LayerProperty));
|
||||
if (propertyInfo != null)
|
||||
LayerProperty.PropertyDescription.Name = propertyInfo.Name.Humanize();
|
||||
else
|
||||
LayerProperty.PropertyDescription.Name = $"Unknown {typeof(T).Name} property";
|
||||
}
|
||||
|
||||
LayerProperty.VisibilityChanged += LayerPropertyOnVisibilityChanged;
|
||||
LayerPropertyTreeViewModel = (ILayerPropertyTreeViewModel) kernel.Get(treeViewModelType, parameter);
|
||||
LayerPropertyTimelineViewModel = (ILayerPropertyTimelineViewModel) kernel.Get(timelineViewModelType, parameter);
|
||||
}
|
||||
|
||||
public override bool IsVisible => !LayerProperty.IsHidden;
|
||||
public ILayerProperty LayerProperty { get; }
|
||||
public ILayerPropertyTreeViewModel LayerPropertyTreeViewModel { get; }
|
||||
public ILayerPropertyTimelineViewModel LayerPropertyTimelineViewModel { get; }
|
||||
|
||||
public LayerProperty<T> LayerProperty { get; }
|
||||
public bool IsVisible { get; set; }
|
||||
public bool IsExpanded { get; set; }
|
||||
|
||||
public TreePropertyViewModel<T> TreePropertyViewModel
|
||||
public void Dispose()
|
||||
{
|
||||
get => _treePropertyViewModel;
|
||||
set => SetAndNotify(ref _treePropertyViewModel, value);
|
||||
}
|
||||
|
||||
public TimelinePropertyViewModel<T> TimelinePropertyViewModel
|
||||
{
|
||||
get => _timelinePropertyViewModel;
|
||||
set => SetAndNotify(ref _timelinePropertyViewModel, value);
|
||||
}
|
||||
|
||||
public override List<BaseLayerPropertyKeyframe> GetKeyframes(bool expandedOnly)
|
||||
{
|
||||
if (LayerProperty.KeyframesEnabled && !LayerProperty.IsHidden)
|
||||
return LayerProperty.BaseKeyframes.ToList();
|
||||
return new List<BaseLayerPropertyKeyframe>();
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
TreePropertyViewModel.Dispose();
|
||||
TimelinePropertyViewModel.Dispose();
|
||||
|
||||
LayerProperty.VisibilityChanged -= LayerPropertyOnVisibilityChanged;
|
||||
}
|
||||
|
||||
public void SetCurrentValue(T value, bool saveChanges)
|
||||
{
|
||||
LayerProperty.SetCurrentValue(value, ProfileEditorService.CurrentTime);
|
||||
if (saveChanges)
|
||||
ProfileEditorService.UpdateSelectedProfileElement();
|
||||
else
|
||||
ProfileEditorService.UpdateProfilePreview();
|
||||
}
|
||||
|
||||
private TreePropertyViewModel<T> CreateTreePropertyViewModel()
|
||||
{
|
||||
// Make sure there is a supported property editor VM, unless the type is an enum, then we'll use the EnumPropertyInputViewModel
|
||||
Type vmType = null;
|
||||
if (typeof(T).IsEnum)
|
||||
vmType = typeof(EnumPropertyInputViewModel<>).MakeGenericType(typeof(T));
|
||||
else
|
||||
{
|
||||
var registration = ProfileEditorService.RegisteredPropertyEditors.FirstOrDefault(r => r.SupportedType == typeof(T));
|
||||
if (registration != null)
|
||||
vmType = registration.ViewModelType;
|
||||
}
|
||||
|
||||
if (vmType == null)
|
||||
throw new ArtemisUIException($"Cannot create a tree property view model for type {typeof(T)}, found no matching property editor");
|
||||
|
||||
var parameters = new IParameter[]
|
||||
{
|
||||
new ConstructorArgument("layerProperty", LayerProperty)
|
||||
};
|
||||
return new TreePropertyViewModel<T>(this, (PropertyInputViewModel<T>) ProfileEditorService.Kernel.Get(vmType, parameters), ProfileEditorService);
|
||||
}
|
||||
|
||||
private void LayerPropertyOnVisibilityChanged(object sender, EventArgs e)
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(IsVisible));
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class LayerPropertyViewModel : LayerPropertyBaseViewModel
|
||||
{
|
||||
private TimelinePropertyViewModel _timelinePropertyBaseViewModel;
|
||||
private TreePropertyViewModel _treePropertyBaseViewModel;
|
||||
|
||||
protected LayerPropertyViewModel(IProfileEditorService profileEditorService, BaseLayerProperty baseLayerProperty)
|
||||
{
|
||||
ProfileEditorService = profileEditorService;
|
||||
BaseLayerProperty = baseLayerProperty;
|
||||
}
|
||||
|
||||
public IProfileEditorService ProfileEditorService { get; }
|
||||
public BaseLayerProperty BaseLayerProperty { get; }
|
||||
|
||||
public TreePropertyViewModel TreePropertyBaseViewModel
|
||||
{
|
||||
get => _treePropertyBaseViewModel;
|
||||
set => SetAndNotify(ref _treePropertyBaseViewModel, value);
|
||||
}
|
||||
|
||||
public TimelinePropertyViewModel TimelinePropertyBaseViewModel
|
||||
{
|
||||
get => _timelinePropertyBaseViewModel;
|
||||
set => SetAndNotify(ref _timelinePropertyBaseViewModel, value);
|
||||
LayerPropertyTreeViewModel?.Dispose();
|
||||
LayerPropertyTimelineViewModel?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
|
||||
{
|
||||
public class LayerPropertyTimelineViewModel<T> : Screen, ILayerPropertyTimelineViewModel
|
||||
{
|
||||
public LayerProperty<T> LayerProperty { get; }
|
||||
public LayerPropertyViewModel LayerPropertyViewModel { get; }
|
||||
|
||||
public LayerPropertyTimelineViewModel(LayerProperty<T> layerProperty, LayerPropertyViewModel layerPropertyViewModel)
|
||||
{
|
||||
LayerProperty = layerProperty;
|
||||
LayerPropertyViewModel = layerPropertyViewModel;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public interface ILayerPropertyTimelineViewModel : IScreen, IDisposable
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Artemis.Core;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline.Rails
|
||||
{
|
||||
public class TimelineGroupViewModel
|
||||
{
|
||||
public LayerPropertyGroup LayerPropertyGroup { get; }
|
||||
|
||||
public TimelineGroupViewModel(LayerPropertyGroup layerPropertyGroup)
|
||||
{
|
||||
LayerPropertyGroup = layerPropertyGroup;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9,7 +9,7 @@
|
||||
xmlns:converters="clr-namespace:Artemis.UI.Converters"
|
||||
xmlns:dd="urn:gong-wpf-dragdrop"
|
||||
mc:Ignorable="d"
|
||||
d:DataContext="{d:DesignInstance local:TreePropertyGroupViewModel}"
|
||||
d:DataContext="{d:DesignInstance local:LayerPropertyGroupTreeViewModel}"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<UserControl.Resources>
|
||||
<converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
|
||||
@ -18,8 +18,8 @@
|
||||
<!-- Type: None -->
|
||||
<TextBlock
|
||||
dd:DragDrop.DragSourceIgnore="True"
|
||||
Text="{Binding LayerPropertyGroupViewModel.PropertyGroupDescription.Name}"
|
||||
ToolTip="{Binding LayerPropertyGroupViewModel.PropertyGroupDescription.Description}"
|
||||
Text="{Binding LayerPropertyGroup.GroupDescription.Name}"
|
||||
ToolTip="{Binding LayerPropertyGroup.GroupDescription.Description}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="3 5 0 5">
|
||||
@ -27,7 +27,7 @@
|
||||
<Style TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding LayerPropertyGroupViewModel.GroupType}" Value="None">
|
||||
<DataTrigger Binding="{Binding GroupType}" Value="None">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
@ -41,14 +41,14 @@
|
||||
<Style TargetType="{x:Type StackPanel}">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding LayerPropertyGroupViewModel.GroupType}" Value="General">
|
||||
<DataTrigger Binding="{Binding GroupType}" Value="General">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</StackPanel.Style>
|
||||
<materialDesign:PackIcon Kind="HammerWrench" Margin="0 -1 5 0" />
|
||||
<TextBlock ToolTip="{Binding LayerPropertyGroupViewModel.PropertyGroupDescription.Description}">General</TextBlock>
|
||||
<TextBlock ToolTip="{Binding LayerPropertyGroup.GroupDescription.Description}">General</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Type: Transform -->
|
||||
@ -57,14 +57,14 @@
|
||||
<Style TargetType="{x:Type StackPanel}">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding LayerPropertyGroupViewModel.GroupType}" Value="Transform">
|
||||
<DataTrigger Binding="{Binding GroupType}" Value="Transform">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</StackPanel.Style>
|
||||
<materialDesign:PackIcon Kind="TransitConnectionVariant" Margin="0 -1 5 0" />
|
||||
<TextBlock ToolTip="{Binding LayerPropertyGroupViewModel.PropertyGroupDescription.Description}">Transform</TextBlock>
|
||||
<TextBlock ToolTip="{Binding LayerPropertyGroup.GroupDescription.Description}">Transform</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Type: LayerBrushRoot -->
|
||||
@ -79,23 +79,23 @@
|
||||
<Style TargetType="{x:Type Grid}">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding LayerPropertyGroupViewModel.GroupType}" Value="LayerBrushRoot">
|
||||
<DataTrigger Binding="{Binding GroupType}" Value="LayerBrushRoot">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Grid.Style>
|
||||
<materialDesign:PackIcon Grid.Column="0"
|
||||
Kind="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerBrush.Descriptor.Icon}"
|
||||
Kind="{Binding LayerPropertyGroup.LayerBrush.Descriptor.Icon}"
|
||||
Margin="0 5 5 0" />
|
||||
<TextBlock Grid.Column="1"
|
||||
ToolTip="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerBrush.Descriptor.Description}"
|
||||
ToolTip="{Binding LayerPropertyGroup.LayerBrush.Descriptor.Description}"
|
||||
Margin="0 5 0 0">
|
||||
Brush - 
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Column="2"
|
||||
Text="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerBrush.Descriptor.DisplayName}"
|
||||
ToolTip="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerBrush.Descriptor.Description}"
|
||||
Text="{Binding LayerPropertyGroup.LayerBrush.Descriptor.DisplayName}"
|
||||
ToolTip="{Binding LayerPropertyGroup.LayerBrush.Descriptor.Description}"
|
||||
Margin="0 5 0 0" />
|
||||
|
||||
<Button Grid.Column="3"
|
||||
@ -106,7 +106,7 @@
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Right"
|
||||
Command="{s:Action OpenBrushSettings}"
|
||||
Visibility="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerBrush.ConfigurationDialog, Converter={StaticResource NullToVisibilityConverter}}">
|
||||
Visibility="{Binding LayerPropertyGroup.LayerBrush.ConfigurationDialog, Converter={StaticResource NullToVisibilityConverter}}">
|
||||
<materialDesign:PackIcon Kind="Settings" Height="16" Width="16" />
|
||||
</Button>
|
||||
</Grid>
|
||||
@ -125,7 +125,7 @@
|
||||
<Style TargetType="{x:Type Grid}">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding LayerPropertyGroupViewModel.GroupType}" Value="LayerEffectRoot">
|
||||
<DataTrigger Binding="{Binding GroupType}" Value="LayerEffectRoot">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
@ -134,29 +134,29 @@
|
||||
<materialDesign:PackIcon
|
||||
Grid.Column="0"
|
||||
Cursor="SizeNS"
|
||||
Kind="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.Descriptor.Icon}"
|
||||
Kind="{Binding LayerPropertyGroup.LayerEffect.Descriptor.Icon}"
|
||||
Margin="0 5 5 0"
|
||||
Background="Transparent" />
|
||||
<TextBlock Grid.Column="1" ToolTip="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.Descriptor.Description}" Margin="0 5 0 0">
|
||||
<TextBlock Grid.Column="1" ToolTip="{Binding LayerPropertyGroup.LayerEffect.Descriptor.Description}" Margin="0 5 0 0">
|
||||
Effect
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Column="2"
|
||||
ToolTip="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.Descriptor.Description}"
|
||||
ToolTip="{Binding LayerPropertyGroup.LayerEffect.Descriptor.Description}"
|
||||
Margin="3 5">
|
||||
-
|
||||
</TextBlock>
|
||||
|
||||
<!-- Show either the descriptors display name or, if set, the effect name -->
|
||||
<TextBlock Grid.Column="3"
|
||||
Text="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.Descriptor.DisplayName}"
|
||||
ToolTip="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.Descriptor.Description}"
|
||||
Text="{Binding LayerPropertyGroup.LayerEffect.Descriptor.DisplayName}"
|
||||
ToolTip="{Binding LayerPropertyGroup.LayerEffect.Descriptor.Description}"
|
||||
Margin="0 5"
|
||||
Visibility="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.Name, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}" />
|
||||
Visibility="{Binding LayerPropertyGroup.LayerEffect.Name, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}" />
|
||||
<TextBlock Grid.Column="4"
|
||||
Text="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.Name}"
|
||||
ToolTip="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.Descriptor.Description}"
|
||||
Text="{Binding LayerPropertyGroup.LayerEffect.Name}"
|
||||
ToolTip="{Binding LayerPropertyGroup.LayerEffect.Descriptor.Description}"
|
||||
Margin="0 5"
|
||||
Visibility="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.Name, Converter={StaticResource NullToVisibilityConverter}}" />
|
||||
Visibility="{Binding LayerPropertyGroup.LayerEffect.Name, Converter={StaticResource NullToVisibilityConverter}}" />
|
||||
|
||||
<StackPanel Grid.Column="5" Orientation="Horizontal">
|
||||
<ToggleButton
|
||||
@ -164,7 +164,7 @@
|
||||
ToolTip="Toggle enabled state"
|
||||
Width="18"
|
||||
Height="18"
|
||||
IsChecked="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.Enabled}"
|
||||
IsChecked="{Binding LayerPropertyGroup.LayerEffect.Enabled}"
|
||||
VerticalAlignment="Center" Padding="-25"
|
||||
Margin="5 0"
|
||||
Command="{s:Action EnableToggled}">
|
||||
@ -184,7 +184,7 @@
|
||||
Height="24"
|
||||
VerticalAlignment="Center"
|
||||
Command="{s:Action OpenEffectSettings}"
|
||||
Visibility="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.ConfigurationDialog, Converter={StaticResource NullToVisibilityConverter}}">
|
||||
Visibility="{Binding LayerPropertyGroup.LayerEffect.ConfigurationDialog, Converter={StaticResource NullToVisibilityConverter}}">
|
||||
<materialDesign:PackIcon Kind="Settings" Height="16" Width="16" />
|
||||
</Button>
|
||||
<Button Style="{StaticResource MaterialDesignIconButton}"
|
||||
@ -1,13 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.LayerBrushes;
|
||||
using Artemis.Core.LayerEffects;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Exceptions;
|
||||
using Artemis.UI.Screens.ProfileEditor.Dialogs;
|
||||
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Abstract;
|
||||
using Artemis.UI.Screens.ProfileEditor.Windows;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Ninject;
|
||||
using Ninject.Parameters;
|
||||
@ -15,34 +14,36 @@ using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
|
||||
{
|
||||
public class TreePropertyGroupViewModel : PropertyChangedBase
|
||||
public class LayerPropertyGroupTreeViewModel : PropertyChangedBase
|
||||
{
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IKernel _kernel;
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private readonly IRenderElementService _renderElementService;
|
||||
private readonly IWindowManager _windowManager;
|
||||
|
||||
public TreePropertyGroupViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel,
|
||||
public LayerPropertyGroupTreeViewModel(
|
||||
LayerPropertyGroupViewModel layerPropertyGroupViewModel,
|
||||
IProfileEditorService profileEditorService,
|
||||
IRenderElementService renderElementService,
|
||||
IDialogService dialogService,
|
||||
IWindowManager windowManager,
|
||||
IKernel kernel)
|
||||
{
|
||||
_profileEditorService = profileEditorService;
|
||||
_renderElementService = renderElementService;
|
||||
_dialogService = dialogService;
|
||||
_windowManager = windowManager;
|
||||
_kernel = kernel;
|
||||
LayerPropertyGroupViewModel = (LayerPropertyGroupViewModel) layerPropertyBaseViewModel;
|
||||
|
||||
LayerPropertyGroupViewModel = layerPropertyGroupViewModel;
|
||||
LayerPropertyGroup = LayerPropertyGroupViewModel.LayerPropertyGroup;
|
||||
}
|
||||
|
||||
public LayerPropertyGroupViewModel LayerPropertyGroupViewModel { get; }
|
||||
public LayerPropertyGroup LayerPropertyGroup { get; }
|
||||
public LayerPropertyGroupType GroupType { get; set; }
|
||||
|
||||
public void OpenBrushSettings()
|
||||
{
|
||||
var layerBrush = LayerPropertyGroupViewModel.LayerPropertyGroup.LayerBrush;
|
||||
var layerBrush = LayerPropertyGroup.LayerBrush;
|
||||
var configurationViewModel = layerBrush.ConfigurationDialog;
|
||||
if (configurationViewModel == null)
|
||||
return;
|
||||
@ -69,7 +70,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
|
||||
|
||||
public void OpenEffectSettings()
|
||||
{
|
||||
var layerEffect = LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect;
|
||||
var layerEffect = LayerPropertyGroup.LayerEffect;
|
||||
var configurationViewModel = layerEffect.ConfigurationDialog;
|
||||
if (configurationViewModel == null)
|
||||
return;
|
||||
@ -100,20 +101,23 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
{"subject", "effect"},
|
||||
{"currentName", LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.Name}
|
||||
{"currentName", LayerPropertyGroup.LayerEffect.Name}
|
||||
}
|
||||
);
|
||||
if (result is string newName)
|
||||
{
|
||||
LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.Name = newName;
|
||||
LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.HasBeenRenamed = true;
|
||||
LayerPropertyGroup.LayerEffect.Name = newName;
|
||||
LayerPropertyGroup.LayerEffect.HasBeenRenamed = true;
|
||||
_profileEditorService.UpdateSelectedProfile();
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteEffect()
|
||||
{
|
||||
_renderElementService.RemoveLayerEffect(LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect);
|
||||
if (LayerPropertyGroup.LayerEffect == null)
|
||||
return;
|
||||
|
||||
LayerPropertyGroup.ProfileElement.RemoveLayerEffect(LayerPropertyGroup.LayerEffect);
|
||||
_profileEditorService.UpdateSelectedProfile();
|
||||
}
|
||||
|
||||
@ -121,5 +125,28 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
|
||||
{
|
||||
_profileEditorService.UpdateSelectedProfile();
|
||||
}
|
||||
|
||||
private void DetermineGroupType()
|
||||
{
|
||||
if (LayerPropertyGroup is LayerGeneralProperties)
|
||||
GroupType = LayerPropertyGroupType.General;
|
||||
else if (LayerPropertyGroup is LayerTransformProperties)
|
||||
GroupType = LayerPropertyGroupType.Transform;
|
||||
else if (LayerPropertyGroup.Parent == null && LayerPropertyGroup.LayerBrush != null)
|
||||
GroupType = LayerPropertyGroupType.LayerBrushRoot;
|
||||
else if (LayerPropertyGroup.Parent == null && LayerPropertyGroup.LayerEffect != null)
|
||||
GroupType = LayerPropertyGroupType.LayerEffectRoot;
|
||||
else
|
||||
GroupType = LayerPropertyGroupType.None;
|
||||
}
|
||||
|
||||
public enum LayerPropertyGroupType
|
||||
{
|
||||
General,
|
||||
Transform,
|
||||
LayerBrushRoot,
|
||||
LayerEffectRoot,
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,12 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree.TreePropertyView"
|
||||
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree.LayerPropertyTreeView"
|
||||
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:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:tree="clr-namespace:Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance {x:Type tree:TreePropertyViewModel}}">
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid Height="22" Margin="-20 0 0 0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
@ -22,7 +20,7 @@
|
||||
Width="18"
|
||||
Height="18"
|
||||
IsChecked="{Binding KeyframesEnabled}"
|
||||
IsEnabled="{Binding LayerPropertyViewModel.LayerProperty.KeyframesSupported}"
|
||||
IsEnabled="{Binding LayerProperty.KeyframesSupported}"
|
||||
VerticalAlignment="Center" Padding="-25">
|
||||
<materialDesign:PackIcon Kind="Stopwatch" Height="13" Width="13" />
|
||||
</ToggleButton>
|
||||
@ -32,8 +30,8 @@
|
||||
Padding="0,0,5,0"
|
||||
VerticalAlignment="Center"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
Text="{Binding LayerPropertyViewModel.LayerProperty.PropertyDescription.Name}"
|
||||
ToolTip="{Binding LayerPropertyViewModel.LayerProperty.PropertyDescription.Description}"
|
||||
Text="{Binding LayerProperty.PropertyDescription.Name}"
|
||||
ToolTip="{Binding LayerProperty.PropertyDescription.Description}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<ContentControl Grid.Column="2" Margin="20 0" s:View.Model="{Binding PropertyInputViewModel}">
|
||||
@ -51,7 +49,7 @@
|
||||
Width="20"
|
||||
Height="20"
|
||||
VerticalAlignment="Center"
|
||||
IsEnabled="{Binding LayerPropertyViewModel.LayerProperty.DataBindingsSupported}"
|
||||
IsEnabled="{Binding LayerProperty.DataBindingsSupported}"
|
||||
IsChecked="{Binding DataBindingsOpen}">
|
||||
<materialDesign:PackIcon Kind="VectorLink" Height="13" Width="13" />
|
||||
</ToggleButton>
|
||||
@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
|
||||
{
|
||||
public class LayerPropertyTreeViewModel<T> : Screen, ILayerPropertyTreeViewModel
|
||||
{
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private PropertyInputViewModel<T> _propertyInputViewModel;
|
||||
|
||||
public LayerPropertyTreeViewModel(LayerProperty<T> layerProperty, LayerPropertyViewModel layerPropertyViewModel, IProfileEditorService profileEditorService)
|
||||
{
|
||||
_profileEditorService = profileEditorService;
|
||||
LayerProperty = layerProperty;
|
||||
LayerPropertyViewModel = layerPropertyViewModel;
|
||||
|
||||
PropertyInputViewModel = _profileEditorService.CreatePropertyInputViewModel(LayerProperty);
|
||||
_profileEditorService.SelectedDataBindingChanged += ProfileEditorServiceOnSelectedDataBindingChanged;
|
||||
}
|
||||
|
||||
public LayerProperty<T> LayerProperty { get; }
|
||||
public LayerPropertyViewModel LayerPropertyViewModel { get; }
|
||||
|
||||
public PropertyInputViewModel<T> PropertyInputViewModel
|
||||
{
|
||||
get => _propertyInputViewModel;
|
||||
set => SetAndNotify(ref _propertyInputViewModel, value);
|
||||
}
|
||||
|
||||
public bool HasPropertyInputViewModel => PropertyInputViewModel != null;
|
||||
|
||||
public bool KeyframesEnabled
|
||||
{
|
||||
get => LayerProperty.KeyframesEnabled;
|
||||
set => ApplyKeyframesEnabled(value);
|
||||
}
|
||||
|
||||
public bool DataBindingsOpen
|
||||
{
|
||||
get => _profileEditorService.SelectedDataBinding == LayerProperty;
|
||||
set => _profileEditorService.ChangeSelectedDataBinding(value ? LayerProperty : null);
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_propertyInputViewModel?.Dispose();
|
||||
_profileEditorService.SelectedDataBindingChanged -= ProfileEditorServiceOnSelectedDataBindingChanged;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void ApplyKeyframesEnabled(bool enable)
|
||||
{
|
||||
// If enabling keyframes for the first time, add a keyframe with the current value at the current position
|
||||
if (enable && !LayerProperty.Keyframes.Any())
|
||||
{
|
||||
LayerProperty.AddKeyframe(new LayerPropertyKeyframe<T>(
|
||||
LayerProperty.CurrentValue,
|
||||
_profileEditorService.CurrentTime,
|
||||
Easings.Functions.Linear,
|
||||
LayerProperty
|
||||
));
|
||||
}
|
||||
// If disabling keyframes, set the base value to the current value
|
||||
else if (!enable && LayerProperty.Keyframes.Any())
|
||||
LayerProperty.BaseValue = LayerProperty.CurrentValue;
|
||||
|
||||
LayerProperty.KeyframesEnabled = enable;
|
||||
|
||||
_profileEditorService.UpdateSelectedProfileElement();
|
||||
}
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void ProfileEditorServiceOnSelectedDataBindingChanged(object sender, EventArgs e)
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(DataBindingsOpen));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public interface ILayerPropertyTreeViewModel : IScreen, IDisposable
|
||||
{
|
||||
bool HasPropertyInputViewModel { get; }
|
||||
}
|
||||
}
|
||||
@ -1,89 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Abstract;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
|
||||
{
|
||||
public class TreePropertyViewModel<T> : TreePropertyViewModel
|
||||
{
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private PropertyInputViewModel<T> _propertyInputViewModel;
|
||||
|
||||
public TreePropertyViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel, PropertyInputViewModel<T> propertyInputViewModel,
|
||||
IProfileEditorService profileEditorService) : base(layerPropertyBaseViewModel)
|
||||
{
|
||||
_profileEditorService = profileEditorService;
|
||||
LayerPropertyViewModel = (LayerPropertyViewModel<T>) layerPropertyBaseViewModel;
|
||||
PropertyInputViewModel = propertyInputViewModel;
|
||||
|
||||
_profileEditorService.SelectedDataBindingChanged += ProfileEditorServiceOnSelectedDataBindingChanged;
|
||||
}
|
||||
|
||||
public LayerPropertyViewModel<T> LayerPropertyViewModel { get; }
|
||||
|
||||
public PropertyInputViewModel<T> PropertyInputViewModel
|
||||
{
|
||||
get => _propertyInputViewModel;
|
||||
set => SetAndNotify(ref _propertyInputViewModel, value);
|
||||
}
|
||||
|
||||
public bool KeyframesEnabled
|
||||
{
|
||||
get => LayerPropertyViewModel.LayerProperty.KeyframesEnabled;
|
||||
set => ApplyKeyframesEnabled(value);
|
||||
}
|
||||
|
||||
public bool DataBindingsOpen
|
||||
{
|
||||
get => _profileEditorService.SelectedDataBinding == LayerPropertyViewModel.BaseLayerProperty;
|
||||
set => _profileEditorService.ChangeSelectedDataBinding(value ? LayerPropertyViewModel.BaseLayerProperty : null);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
PropertyInputViewModel.Dispose();
|
||||
}
|
||||
|
||||
private void ProfileEditorServiceOnSelectedDataBindingChanged(object sender, EventArgs e)
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(DataBindingsOpen));
|
||||
}
|
||||
|
||||
private void ApplyKeyframesEnabled(bool enable)
|
||||
{
|
||||
// If enabling keyframes for the first time, add a keyframe with the current value at the current position
|
||||
if (enable && !LayerPropertyViewModel.LayerProperty.Keyframes.Any())
|
||||
{
|
||||
LayerPropertyViewModel.LayerProperty.AddKeyframe(new LayerPropertyKeyframe<T>(
|
||||
LayerPropertyViewModel.LayerProperty.CurrentValue,
|
||||
_profileEditorService.CurrentTime,
|
||||
Easings.Functions.Linear,
|
||||
LayerPropertyViewModel.LayerProperty
|
||||
));
|
||||
}
|
||||
// If disabling keyframes, set the base value to the current value
|
||||
else if (!enable && LayerPropertyViewModel.LayerProperty.Keyframes.Any())
|
||||
LayerPropertyViewModel.LayerProperty.BaseValue = LayerPropertyViewModel.LayerProperty.CurrentValue;
|
||||
|
||||
LayerPropertyViewModel.LayerProperty.KeyframesEnabled = enable;
|
||||
|
||||
_profileEditorService.UpdateSelectedProfileElement();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class TreePropertyViewModel : PropertyChangedBase, IDisposable
|
||||
{
|
||||
protected TreePropertyViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel)
|
||||
{
|
||||
LayerPropertyBaseViewModel = layerPropertyBaseViewModel;
|
||||
}
|
||||
|
||||
public LayerPropertyBaseViewModel LayerPropertyBaseViewModel { get; }
|
||||
|
||||
public abstract void Dispose();
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,7 @@
|
||||
xmlns:converters="clr-namespace:Artemis.UI.Converters"
|
||||
xmlns:dd="urn:gong-wpf-dragdrop"
|
||||
xmlns:tree="clr-namespace:Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree"
|
||||
xmlns:layerProperties1="clr-namespace:Artemis.UI.Screens.ProfileEditor.LayerProperties"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.LayerProperties"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance {x:Type tree:TreeViewModel}}">
|
||||
@ -98,11 +98,11 @@
|
||||
</Style>
|
||||
</TreeView.ItemContainerStyle>
|
||||
<TreeView.Resources>
|
||||
<HierarchicalDataTemplate DataType="{x:Type layerProperties1:LayerPropertyGroupViewModel}" ItemsSource="{Binding Children}">
|
||||
<ContentControl s:View.Model="{Binding TreePropertyGroupViewModel}" />
|
||||
<HierarchicalDataTemplate DataType="{x:Type local:LayerPropertyGroupViewModel}" ItemsSource="{Binding Items}">
|
||||
<ContentControl s:View.Model="{Binding LayerPropertyGroupTreeViewModel}" />
|
||||
</HierarchicalDataTemplate>
|
||||
<DataTemplate DataType="{x:Type layerProperties1:LayerPropertyViewModel}">
|
||||
<ContentControl s:View.Model="{Binding TreePropertyBaseViewModel}" dd:DragDrop.DragSourceIgnore="True" />
|
||||
<DataTemplate DataType="{x:Type local:LayerPropertyViewModel}">
|
||||
<ContentControl s:View.Model="{Binding LayerPropertyTreeViewModel}" dd:DragDrop.DragSourceIgnore="True" />
|
||||
</DataTemplate>
|
||||
</TreeView.Resources>
|
||||
</TreeView>
|
||||
|
||||
@ -5,11 +5,13 @@ using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
|
||||
{
|
||||
public class TreeViewModel : PropertyChangedBase
|
||||
public class TreeViewModel : Screen
|
||||
{
|
||||
public TreeViewModel(LayerPropertiesViewModel layerPropertiesViewModel, BindableCollection<LayerPropertyGroupViewModel> layerPropertyGroups)
|
||||
{
|
||||
LayerPropertiesViewModel = layerPropertiesViewModel;
|
||||
|
||||
// Not using the Items collection because the list should persist even after this VM gets closed
|
||||
LayerPropertyGroups = layerPropertyGroups;
|
||||
}
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor
|
||||
{
|
||||
public class ProfileEditorViewModel : Conductor<ProfileEditorPanelViewModel>.Collection.AllActive
|
||||
public class ProfileEditorViewModel : Conductor<IProfileEditorPanelViewModel>.Collection.AllActive
|
||||
{
|
||||
private readonly IModuleService _moduleService;
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
@ -36,7 +36,7 @@ namespace Artemis.UI.Screens.ProfileEditor
|
||||
private PluginSetting<GridLength> _sidePanelsWidth;
|
||||
|
||||
public ProfileEditorViewModel(ProfileModule module,
|
||||
ICollection<ProfileEditorPanelViewModel> viewModels,
|
||||
ICollection<IProfileEditorPanelViewModel> viewModels,
|
||||
IProfileEditorService profileEditorService,
|
||||
IProfileService profileService,
|
||||
IDialogService dialogService,
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
</StackPanel>
|
||||
|
||||
<TreeView Grid.Row="1"
|
||||
ItemsSource="{Binding RootFolder.Children}"
|
||||
ItemsSource="{Binding ActiveItem.Items}"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
dd:DragDrop.IsDragSource="True"
|
||||
dd:DragDrop.IsDropTarget="True"
|
||||
@ -36,11 +36,11 @@
|
||||
<behaviors:TreeViewSelectionBehavior ExpandSelected="True" SelectedItem="{Binding SelectedTreeItem}" />
|
||||
</b:Interaction.Behaviors>
|
||||
<TreeView.Resources>
|
||||
<HierarchicalDataTemplate DataType="{x:Type treeItem1:FolderViewModel}" ItemsSource="{Binding Children}">
|
||||
<HierarchicalDataTemplate DataType="{x:Type treeItem1:FolderViewModel}" ItemsSource="{Binding Items}">
|
||||
<ContentControl s:View.Model="{Binding}" />
|
||||
</HierarchicalDataTemplate>
|
||||
<!-- TODO: Ensure this item source is required -->
|
||||
<HierarchicalDataTemplate DataType="{x:Type treeItem1:LayerViewModel}" ItemsSource="{Binding Children}">
|
||||
<HierarchicalDataTemplate DataType="{x:Type treeItem1:LayerViewModel}" ItemsSource="{Binding Items}">
|
||||
<ContentControl s:View.Model="{Binding}" />
|
||||
</HierarchicalDataTemplate>
|
||||
</TreeView.Resources>
|
||||
|
||||
@ -11,24 +11,17 @@ using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
||||
{
|
||||
public class ProfileTreeViewModel : ProfileEditorPanelViewModel, IDropTarget
|
||||
public class ProfileTreeViewModel : Conductor<FolderViewModel>, IProfileEditorPanelViewModel, IDropTarget
|
||||
{
|
||||
private readonly IFolderVmFactory _folderVmFactory;
|
||||
private readonly IProfileTreeVmFactory _profileTreeVmFactory;
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private FolderViewModel _rootFolder;
|
||||
private TreeItemViewModel _selectedTreeItem;
|
||||
private bool _updatingTree;
|
||||
|
||||
public ProfileTreeViewModel(IProfileEditorService profileEditorService, IFolderVmFactory folderVmFactory)
|
||||
public ProfileTreeViewModel(IProfileEditorService profileEditorService, IProfileTreeVmFactory profileTreeVmFactory)
|
||||
{
|
||||
_profileEditorService = profileEditorService;
|
||||
_folderVmFactory = folderVmFactory;
|
||||
}
|
||||
|
||||
public FolderViewModel RootFolder
|
||||
{
|
||||
get => _rootFolder;
|
||||
set => SetAndNotify(ref _rootFolder, value);
|
||||
_profileTreeVmFactory = profileTreeVmFactory;
|
||||
}
|
||||
|
||||
public TreeItemViewModel SelectedTreeItem
|
||||
@ -73,7 +66,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
||||
switch (dragDropType)
|
||||
{
|
||||
case DragDropType.Add:
|
||||
source.Parent.RemoveExistingElement(source);
|
||||
((TreeItemViewModel) source.Parent).RemoveExistingElement(source);
|
||||
target.AddExistingElement(source);
|
||||
break;
|
||||
case DragDropType.InsertBefore:
|
||||
@ -92,27 +85,25 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
||||
// ReSharper disable once UnusedMember.Global - Called from view
|
||||
public void AddFolder()
|
||||
{
|
||||
RootFolder?.AddFolder();
|
||||
ActiveItem?.AddFolder();
|
||||
}
|
||||
|
||||
// ReSharper disable once UnusedMember.Global - Called from view
|
||||
public void AddLayer()
|
||||
{
|
||||
RootFolder?.AddLayer();
|
||||
ActiveItem?.AddLayer();
|
||||
}
|
||||
|
||||
protected override void OnInitialActivate()
|
||||
{
|
||||
Subscribe();
|
||||
CreateRootFolderViewModel();
|
||||
base.OnInitialActivate();
|
||||
}
|
||||
|
||||
protected override void OnClose()
|
||||
{
|
||||
Unsubscribe();
|
||||
|
||||
RootFolder?.Dispose();
|
||||
RootFolder = null;
|
||||
base.OnClose();
|
||||
}
|
||||
|
||||
@ -122,12 +113,11 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
||||
var firstChild = _profileEditorService.SelectedProfile?.Children?.FirstOrDefault();
|
||||
if (!(firstChild is Folder folder))
|
||||
{
|
||||
RootFolder = null;
|
||||
ActivateItem(null);
|
||||
return;
|
||||
}
|
||||
|
||||
RootFolder?.Dispose();
|
||||
RootFolder = _folderVmFactory.Create(folder);
|
||||
ActivateItem(_profileTreeVmFactory.FolderViewModel(folder));
|
||||
_updatingTree = false;
|
||||
|
||||
// Auto-select the first layer
|
||||
@ -150,7 +140,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
||||
{
|
||||
if (parent == source)
|
||||
return DragDropType.None;
|
||||
parent = parent.Parent;
|
||||
parent = (TreeItemViewModel) parent.Parent;
|
||||
}
|
||||
|
||||
switch (dropInfo.InsertPosition)
|
||||
@ -186,20 +176,20 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
||||
if (e.RenderProfileElement == SelectedTreeItem?.ProfileElement)
|
||||
return;
|
||||
|
||||
if (RootFolder == null)
|
||||
if (ActiveItem == null)
|
||||
{
|
||||
CreateRootFolderViewModel();
|
||||
return;
|
||||
}
|
||||
|
||||
_updatingTree = true;
|
||||
RootFolder.UpdateProfileElements();
|
||||
ActiveItem.UpdateProfileElements();
|
||||
_updatingTree = false;
|
||||
if (e.RenderProfileElement == null)
|
||||
SelectedTreeItem = null;
|
||||
else
|
||||
{
|
||||
var match = RootFolder.GetAllChildren().FirstOrDefault(vm => vm.ProfileElement == e.RenderProfileElement);
|
||||
var match = ActiveItem.GetAllChildren().FirstOrDefault(vm => vm.ProfileElement == e.RenderProfileElement);
|
||||
if (match != null)
|
||||
SelectedTreeItem = match;
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Shared.Services;
|
||||
|
||||
@ -11,36 +10,11 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
|
||||
public FolderViewModel(ProfileElement folder,
|
||||
IProfileEditorService profileEditorService,
|
||||
IDialogService dialogService,
|
||||
IRenderElementService renderElementService,
|
||||
IFolderVmFactory folderVmFactory,
|
||||
ILayerVmFactory layerVmFactory) :
|
||||
base(null, folder, profileEditorService, dialogService, renderElementService, folderVmFactory, layerVmFactory)
|
||||
{
|
||||
}
|
||||
|
||||
public FolderViewModel(TreeItemViewModel parent,
|
||||
ProfileElement folder,
|
||||
IProfileEditorService profileEditorService,
|
||||
IDialogService dialogService,
|
||||
IRenderElementService renderElementService,
|
||||
IFolderVmFactory folderVmFactory,
|
||||
ILayerVmFactory layerVmFactory) :
|
||||
base(parent, folder, profileEditorService, dialogService, renderElementService, folderVmFactory, layerVmFactory)
|
||||
IProfileTreeVmFactory profileTreeVmFactory) :
|
||||
base(folder, profileEditorService, dialogService, profileTreeVmFactory)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool SupportsChildren => true;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
foreach (var treeItemViewModel in Children)
|
||||
treeItemViewModel.Dispose();
|
||||
Children.Clear();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,4 @@
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Shared.Services;
|
||||
|
||||
@ -7,14 +6,11 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
|
||||
{
|
||||
public class LayerViewModel : TreeItemViewModel
|
||||
{
|
||||
public LayerViewModel(TreeItemViewModel parent,
|
||||
ProfileElement folder,
|
||||
public LayerViewModel(ProfileElement layer,
|
||||
IProfileEditorService profileEditorService,
|
||||
IDialogService dialogService,
|
||||
IRenderElementService renderElementService,
|
||||
IFolderVmFactory folderVmFactory,
|
||||
ILayerVmFactory layerVmFactory) :
|
||||
base(parent, folder, profileEditorService, dialogService, renderElementService, folderVmFactory, layerVmFactory)
|
||||
IProfileTreeVmFactory profileTreeVmFactory) :
|
||||
base(layer, profileEditorService, dialogService, profileTreeVmFactory)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Exceptions;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Screens.ProfileEditor.Dialogs;
|
||||
@ -12,66 +11,45 @@ using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
|
||||
{
|
||||
public abstract class TreeItemViewModel : PropertyChangedBase, IDisposable
|
||||
public abstract class TreeItemViewModel : Conductor<TreeItemViewModel>.Collection.AllActive, IDisposable
|
||||
{
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IFolderVmFactory _folderVmFactory;
|
||||
private readonly ILayerVmFactory _layerVmFactory;
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private readonly IRenderElementService _renderElementService;
|
||||
private TreeItemViewModel _parent;
|
||||
private readonly IProfileTreeVmFactory _profileTreeVmFactory;
|
||||
private ProfileElement _profileElement;
|
||||
|
||||
protected TreeItemViewModel(TreeItemViewModel parent,
|
||||
ProfileElement profileElement,
|
||||
protected TreeItemViewModel(ProfileElement profileElement,
|
||||
IProfileEditorService profileEditorService,
|
||||
IDialogService dialogService,
|
||||
IRenderElementService renderElementService,
|
||||
IFolderVmFactory folderVmFactory,
|
||||
ILayerVmFactory layerVmFactory)
|
||||
IProfileTreeVmFactory profileTreeVmFactory)
|
||||
{
|
||||
_profileEditorService = profileEditorService;
|
||||
_dialogService = dialogService;
|
||||
_renderElementService = renderElementService;
|
||||
_folderVmFactory = folderVmFactory;
|
||||
_layerVmFactory = layerVmFactory;
|
||||
_profileTreeVmFactory = profileTreeVmFactory;
|
||||
|
||||
Parent = parent;
|
||||
ProfileElement = profileElement;
|
||||
|
||||
Children = new BindableCollection<TreeItemViewModel>();
|
||||
|
||||
Subscribe();
|
||||
UpdateProfileElements();
|
||||
}
|
||||
|
||||
public TreeItemViewModel Parent
|
||||
{
|
||||
get => _parent;
|
||||
set => SetAndNotify(ref _parent, value);
|
||||
}
|
||||
|
||||
public ProfileElement ProfileElement
|
||||
{
|
||||
get => _profileElement;
|
||||
set => SetAndNotify(ref _profileElement, value);
|
||||
}
|
||||
|
||||
public BindableCollection<TreeItemViewModel> Children { get; }
|
||||
|
||||
public abstract bool SupportsChildren { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Unsubscribe();
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public List<TreeItemViewModel> GetAllChildren()
|
||||
{
|
||||
var children = new List<TreeItemViewModel>();
|
||||
foreach (var childFolder in Children)
|
||||
foreach (var childFolder in Items)
|
||||
{
|
||||
// Add all children in this element
|
||||
children.Add(childFolder);
|
||||
@ -84,34 +62,38 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
|
||||
|
||||
public void SetElementInFront(TreeItemViewModel source)
|
||||
{
|
||||
var sourceParent = (TreeItemViewModel) source.Parent;
|
||||
var parent = (TreeItemViewModel) Parent;
|
||||
if (source.Parent != Parent)
|
||||
{
|
||||
source.Parent.RemoveExistingElement(source);
|
||||
Parent.AddExistingElement(source);
|
||||
sourceParent.RemoveExistingElement(source);
|
||||
parent.AddExistingElement(source);
|
||||
}
|
||||
|
||||
Parent.Unsubscribe();
|
||||
Parent.ProfileElement.RemoveChild(source.ProfileElement);
|
||||
Parent.ProfileElement.AddChild(source.ProfileElement, ProfileElement.Order);
|
||||
Parent.Subscribe();
|
||||
parent.Unsubscribe();
|
||||
parent.ProfileElement.RemoveChild(source.ProfileElement);
|
||||
parent.ProfileElement.AddChild(source.ProfileElement, ProfileElement.Order);
|
||||
parent.Subscribe();
|
||||
|
||||
Parent.UpdateProfileElements();
|
||||
parent.UpdateProfileElements();
|
||||
}
|
||||
|
||||
public void SetElementBehind(TreeItemViewModel source)
|
||||
{
|
||||
var sourceParent = (TreeItemViewModel) source.Parent;
|
||||
var parent = (TreeItemViewModel) Parent;
|
||||
if (source.Parent != Parent)
|
||||
{
|
||||
source.Parent.RemoveExistingElement(source);
|
||||
Parent.AddExistingElement(source);
|
||||
sourceParent.RemoveExistingElement(source);
|
||||
parent.AddExistingElement(source);
|
||||
}
|
||||
|
||||
Parent.Unsubscribe();
|
||||
Parent.ProfileElement.RemoveChild(source.ProfileElement);
|
||||
Parent.ProfileElement.AddChild(source.ProfileElement, ProfileElement.Order + 1);
|
||||
Parent.Subscribe();
|
||||
parent.Unsubscribe();
|
||||
parent.ProfileElement.RemoveChild(source.ProfileElement);
|
||||
parent.ProfileElement.AddChild(source.ProfileElement, ProfileElement.Order + 1);
|
||||
parent.Subscribe();
|
||||
|
||||
Parent.UpdateProfileElements();
|
||||
parent.UpdateProfileElements();
|
||||
}
|
||||
|
||||
public void RemoveExistingElement(TreeItemViewModel treeItem)
|
||||
@ -138,7 +120,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
|
||||
if (!SupportsChildren)
|
||||
throw new ArtemisUIException("Cannot add a folder to a profile element of type " + ProfileElement.GetType().Name);
|
||||
|
||||
ProfileElement.AddChild(new Folder(ProfileElement.Profile, ProfileElement, "New folder"));
|
||||
var _ = new Folder(ProfileElement, "New folder");
|
||||
_profileEditorService.UpdateSelectedProfile();
|
||||
}
|
||||
|
||||
@ -147,7 +129,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
|
||||
if (!SupportsChildren)
|
||||
throw new ArtemisUIException("Cannot add a layer to a profile element of type " + ProfileElement.GetType().Name);
|
||||
|
||||
_renderElementService.CreateLayer(ProfileElement.Profile, ProfileElement, "New layer");
|
||||
var _ = new Layer(ProfileElement, "New layer");
|
||||
_profileEditorService.UpdateSelectedProfile();
|
||||
}
|
||||
|
||||
@ -180,7 +162,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
|
||||
return;
|
||||
|
||||
// Farewell, cruel world
|
||||
var parent = Parent;
|
||||
var parent = (TreeItemViewModel) Parent;
|
||||
ProfileElement.Parent?.RemoveChild(ProfileElement);
|
||||
parent.RemoveExistingElement(this);
|
||||
|
||||
@ -190,17 +172,17 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
|
||||
public void UpdateProfileElements()
|
||||
{
|
||||
// Remove VMs that are no longer a child
|
||||
var toRemove = Children.Where(c => c.ProfileElement.Parent != ProfileElement).ToList();
|
||||
var toRemove = Items.Where(c => c.ProfileElement.Parent != ProfileElement).ToList();
|
||||
foreach (var treeItemViewModel in toRemove)
|
||||
Children.Remove(treeItemViewModel);
|
||||
DeactivateItem(treeItemViewModel);
|
||||
|
||||
// Order the children
|
||||
var vmsList = Children.OrderBy(v => v.ProfileElement.Order).ToList();
|
||||
var vmsList = Items.OrderBy(v => v.ProfileElement.Order).ToList();
|
||||
for (var index = 0; index < vmsList.Count; index++)
|
||||
{
|
||||
var profileElementViewModel = vmsList[index];
|
||||
if (Children.IndexOf(profileElementViewModel) != index)
|
||||
Children.Move(Children.IndexOf(profileElementViewModel), index);
|
||||
if (Items.IndexOf(profileElementViewModel) != index)
|
||||
((BindableCollection<TreeItemViewModel>) Items).Move(Items.IndexOf(profileElementViewModel), index);
|
||||
}
|
||||
|
||||
// Ensure every child element has an up-to-date VM
|
||||
@ -212,13 +194,13 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
|
||||
{
|
||||
if (profileElement is Folder folder)
|
||||
{
|
||||
if (Children.FirstOrDefault(p => p is FolderViewModel vm && vm.ProfileElement == folder) == null)
|
||||
newChildren.Add(_folderVmFactory.Create((FolderViewModel) this, folder));
|
||||
if (Items.FirstOrDefault(p => p is FolderViewModel vm && vm.ProfileElement == folder) == null)
|
||||
newChildren.Add(_profileTreeVmFactory.FolderViewModel(folder));
|
||||
}
|
||||
else if (profileElement is Layer layer)
|
||||
{
|
||||
if (Children.FirstOrDefault(p => p is LayerViewModel vm && vm.ProfileElement == layer) == null)
|
||||
newChildren.Add(_layerVmFactory.Create((FolderViewModel) this, layer));
|
||||
if (Items.FirstOrDefault(p => p is LayerViewModel vm && vm.ProfileElement == layer) == null)
|
||||
newChildren.Add(_profileTreeVmFactory.LayerViewModel(layer));
|
||||
}
|
||||
}
|
||||
|
||||
@ -229,7 +211,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
|
||||
foreach (var treeItemViewModel in newChildren)
|
||||
{
|
||||
treeItemViewModel.UpdateProfileElements();
|
||||
Children.Add(treeItemViewModel);
|
||||
ActivateItem(treeItemViewModel);
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,13 +220,6 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
|
||||
_profileEditorService.UpdateSelectedProfile();
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void Subscribe()
|
||||
{
|
||||
ProfileElement.ChildAdded += ProfileElementOnChildAdded;
|
||||
|
||||
@ -1,60 +0,0 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.Visualization.ProfileDeviceView"
|
||||
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:s="https://github.com/canton7/Stylet"
|
||||
xmlns:converters="clr-namespace:Artemis.UI.Converters"
|
||||
xmlns:visualization1="clr-namespace:Artemis.UI.Screens.ProfileEditor.Visualization"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance {x:Type visualization1:ProfileDeviceViewModel}}">
|
||||
<UserControl.Resources>
|
||||
<converters:NullToImageConverter x:Key="NullToImageConverter" />
|
||||
<converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid Width="{Binding Device.RgbDevice.ActualSize.Width}" Height="{Binding Device.RgbDevice.ActualSize.Height}">
|
||||
<Grid.RenderTransform>
|
||||
<RotateTransform Angle="{Binding Device.Rotation}" />
|
||||
</Grid.RenderTransform>
|
||||
|
||||
<!-- Device image with fallback -->
|
||||
<Image VerticalAlignment="Stretch"
|
||||
HorizontalAlignment="Stretch"
|
||||
Source="{Binding Device.RgbDevice.DeviceInfo.Image, Converter={StaticResource NullToImageConverter}}" />
|
||||
|
||||
<Rectangle Fill="{DynamicResource MaterialDesignCardBackground}"
|
||||
Stroke="{DynamicResource MaterialDesignTextBoxBorder}"
|
||||
Margin="-5"
|
||||
StrokeThickness="1"
|
||||
Visibility="{Binding Device.RgbDevice.DeviceInfo.Image, ConverterParameter=Inverted, Converter={StaticResource NullToVisibilityConverter}}" />
|
||||
<TextBlock Text="{Binding Device.RgbDevice.DeviceInfo.DeviceName}"
|
||||
Visibility="{Binding Device.RgbDevice.DeviceInfo.Image, ConverterParameter=Inverted, Converter={StaticResource NullToVisibilityConverter}}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
TextWrapping="Wrap"
|
||||
TextAlignment="Center" />
|
||||
|
||||
<!-- LEDs -->
|
||||
<ItemsControl ItemsSource="{Binding Leds}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<Canvas />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type ContentPresenter}">
|
||||
<Setter Property="Canvas.Left" Value="{Binding X}" />
|
||||
<Setter Property="Canvas.Top" Value="{Binding Y}" />
|
||||
</Style>
|
||||
</ItemsControl.ItemContainerStyle>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ContentControl Width="{Binding Width}" Height="{Binding Height}" s:View.Model="{Binding}" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@ -1,96 +0,0 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using Artemis.Core;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.Visualization
|
||||
{
|
||||
public class ProfileDeviceViewModel : CanvasViewModel
|
||||
{
|
||||
private bool _addedLeds;
|
||||
private ArtemisDevice _device;
|
||||
private ObservableCollection<ProfileLedViewModel> _leds;
|
||||
|
||||
public ProfileDeviceViewModel(ArtemisDevice device)
|
||||
{
|
||||
Device = device;
|
||||
Leds = new ObservableCollection<ProfileLedViewModel>();
|
||||
|
||||
Task.Run(AddLedsAsync);
|
||||
}
|
||||
|
||||
public ObservableCollection<ProfileLedViewModel> Leds
|
||||
{
|
||||
get => _leds;
|
||||
set => SetAndNotify(ref _leds, value);
|
||||
}
|
||||
|
||||
public ArtemisDevice Device
|
||||
{
|
||||
get => _device;
|
||||
set => SetAndNotify(ref _device, value);
|
||||
}
|
||||
|
||||
public bool AddedLeds
|
||||
{
|
||||
get => _addedLeds;
|
||||
private set => SetAndNotify(ref _addedLeds, value);
|
||||
}
|
||||
|
||||
public new double X
|
||||
{
|
||||
get => Device.X;
|
||||
set => Device.X = value;
|
||||
}
|
||||
|
||||
public new double Y
|
||||
{
|
||||
get => Device.Y;
|
||||
set => Device.Y = value;
|
||||
}
|
||||
|
||||
public int ZIndex
|
||||
{
|
||||
get => Device.ZIndex;
|
||||
set => Device.ZIndex = value;
|
||||
}
|
||||
|
||||
|
||||
public Rect DeviceRectangle => Device.RgbDevice == null
|
||||
? new Rect()
|
||||
: new Rect(X, Y, Device.RgbDevice.Size.Width, Device.RgbDevice.Size.Height);
|
||||
|
||||
/// <summary>
|
||||
/// Update the color of all LEDs if finished adding
|
||||
/// </summary>
|
||||
public void Update()
|
||||
{
|
||||
if (!AddedLeds)
|
||||
return;
|
||||
|
||||
foreach (var ledViewModel in Leds)
|
||||
ledViewModel.Update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds LEDs in batches of 5 to avoid UI freezes
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task AddLedsAsync()
|
||||
{
|
||||
var index = 0;
|
||||
foreach (var led in Device.Leds.ToList())
|
||||
{
|
||||
Execute.OnUIThreadSync(() => Leds.Add(new ProfileLedViewModel(led)));
|
||||
if (index % 5 == 0)
|
||||
await Task.Delay(1);
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
AddedLeds = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,93 +0,0 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.Visualization.ProfileLedView"
|
||||
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:converters="clr-namespace:Artemis.UI.Converters"
|
||||
xmlns:visualization1="clr-namespace:Artemis.UI.Screens.ProfileEditor.Visualization"
|
||||
mc:Ignorable="d"
|
||||
d:DataContext="{d:DesignInstance {x:Type visualization1:ProfileLedViewModel}}"
|
||||
d:DesignHeight="25" d:DesignWidth="25">
|
||||
<UserControl.Resources>
|
||||
<converters:NullToImageConverter x:Key="NullToImageConverter" />
|
||||
<Style TargetType="{x:Type Path}" x:Key="DimStyle">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsDimmed}" Value="True">
|
||||
<DataTrigger.EnterActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetProperty="(Shape.Opacity)" To="0.2" Duration="0:0:0.25" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</DataTrigger.EnterActions>
|
||||
<DataTrigger.ExitActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetProperty="(Shape.Opacity)" To="1" Duration="0:0:0.25" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</DataTrigger.ExitActions>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
<Canvas Width="{Binding Led.RgbLed.ActualSize.Width, Mode=OneWay}" Height="{Binding Led.RgbLed.ActualSize.Height, Mode=OneWay}">
|
||||
<Canvas.Background>
|
||||
<ImageBrush AlignmentX="Center" AlignmentY="Center" Stretch="Fill"
|
||||
ImageSource="{Binding Led.RgbLed.Image, Converter={StaticResource NullToImageConverter}, Mode=OneWay}" />
|
||||
</Canvas.Background>
|
||||
|
||||
<Path Data="{Binding DisplayGeometry, Mode=OneWay}" ClipToBounds="False" Style="{StaticResource DimStyle}">
|
||||
<Path.Fill>
|
||||
<SolidColorBrush Color="{Binding DisplayColor, Mode=OneWay}" Opacity="0.333" />
|
||||
</Path.Fill>
|
||||
</Path>
|
||||
<Path Data="{Binding StrokeGeometry, Mode=OneWay}" ClipToBounds="False" Style="{StaticResource DimStyle}">
|
||||
<Path.Fill>
|
||||
<SolidColorBrush Color="{Binding DisplayColor, Mode=OneWay}" />
|
||||
</Path.Fill>
|
||||
</Path>
|
||||
|
||||
<!-- Selection -->
|
||||
<Path Data="{Binding DisplayGeometry, Mode=OneWay}" ClipToBounds="False" StrokeThickness="1" StrokeLineJoin="Round">
|
||||
<Path.Style>
|
||||
<Style TargetType="{x:Type Path}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsSelected}" Value="True">
|
||||
<DataTrigger.EnterActions>
|
||||
<BeginStoryboard x:Name="ToSelected">
|
||||
<Storyboard>
|
||||
<ColorAnimation
|
||||
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
|
||||
To="{StaticResource Accent700}" Duration="0:0:0.25" />
|
||||
<ColorAnimation
|
||||
Storyboard.TargetProperty="(Shape.Stroke).(SolidColorBrush.Color)"
|
||||
To="{StaticResource Accent400}" Duration="0:0:0.25" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</DataTrigger.EnterActions>
|
||||
<DataTrigger.ExitActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<ColorAnimation
|
||||
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
|
||||
To="Transparent" Duration="0:0:0.25" />
|
||||
<ColorAnimation
|
||||
Storyboard.TargetProperty="(Shape.Stroke).(SolidColorBrush.Color)"
|
||||
To="Transparent" Duration="0:0:0.25" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</DataTrigger.ExitActions>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Path.Style>
|
||||
<Path.Fill>
|
||||
<SolidColorBrush Opacity="0.25" />
|
||||
</Path.Fill>
|
||||
<Path.Stroke>
|
||||
<SolidColorBrush Opacity="0.5" />
|
||||
</Path.Stroke>
|
||||
</Path>
|
||||
</Canvas>
|
||||
</UserControl>
|
||||
@ -1,148 +0,0 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Extensions;
|
||||
using RGB.NET.Core;
|
||||
using Stylet;
|
||||
using Color = System.Windows.Media.Color;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.Visualization
|
||||
{
|
||||
public class ProfileLedViewModel : PropertyChangedBase
|
||||
{
|
||||
private Color _displayColor;
|
||||
private Geometry _displayGeometry;
|
||||
private bool _isDimmed;
|
||||
private bool _isSelected;
|
||||
private Geometry _strokeGeometry;
|
||||
|
||||
public ProfileLedViewModel(ArtemisLed led)
|
||||
{
|
||||
Led = led;
|
||||
|
||||
// Don't want ActualLocation here since rotation is done in XAML
|
||||
X = led.RgbLed.Location.X * led.RgbLed.Device.Scale.Horizontal;
|
||||
Y = led.RgbLed.Location.Y * led.RgbLed.Device.Scale.Vertical;
|
||||
Width = led.RgbLed.ActualSize.Width;
|
||||
Height = led.RgbLed.ActualSize.Height;
|
||||
|
||||
Execute.PostToUIThread(CreateLedGeometry);
|
||||
}
|
||||
|
||||
public ArtemisLed Led { get; }
|
||||
|
||||
public double X { get; }
|
||||
public double Y { get; }
|
||||
public double Width { get; }
|
||||
public double Height { get; }
|
||||
|
||||
public bool IsSelected
|
||||
{
|
||||
get => _isSelected;
|
||||
set => SetAndNotify(ref _isSelected, value);
|
||||
}
|
||||
|
||||
public bool IsDimmed
|
||||
{
|
||||
get => _isDimmed;
|
||||
set => SetAndNotify(ref _isDimmed, value);
|
||||
}
|
||||
|
||||
public Geometry DisplayGeometry
|
||||
{
|
||||
get => _displayGeometry;
|
||||
private set => SetAndNotify(ref _displayGeometry, value);
|
||||
}
|
||||
|
||||
public Geometry StrokeGeometry
|
||||
{
|
||||
get => _strokeGeometry;
|
||||
private set => SetAndNotify(ref _strokeGeometry, value);
|
||||
}
|
||||
|
||||
public Color DisplayColor
|
||||
{
|
||||
get => _displayColor;
|
||||
private set => SetAndNotify(ref _displayColor, value);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
var newColor = Led.RgbLed.Color.ToMediaColor();
|
||||
Execute.PostToUIThread(() =>
|
||||
{
|
||||
if (!DisplayColor.Equals(newColor))
|
||||
DisplayColor = newColor;
|
||||
});
|
||||
}
|
||||
|
||||
private void CreateLedGeometry()
|
||||
{
|
||||
switch (Led.RgbLed.Shape)
|
||||
{
|
||||
case Shape.Custom:
|
||||
if (Led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keyboard || Led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keypad)
|
||||
CreateCustomGeometry(2.0);
|
||||
else
|
||||
CreateCustomGeometry(1.0);
|
||||
break;
|
||||
case Shape.Rectangle:
|
||||
if (Led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keyboard || Led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keypad)
|
||||
CreateKeyCapGeometry();
|
||||
else
|
||||
CreateRectangleGeometry();
|
||||
break;
|
||||
case Shape.Circle:
|
||||
CreateCircleGeometry();
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
// Stroke geometry is the display geometry excluding the inner geometry
|
||||
StrokeGeometry = DisplayGeometry.GetWidenedPathGeometry(new Pen(null, 1.0), 0.1, ToleranceType.Absolute);
|
||||
DisplayGeometry.Freeze();
|
||||
StrokeGeometry.Freeze();
|
||||
}
|
||||
|
||||
private void CreateRectangleGeometry()
|
||||
{
|
||||
DisplayGeometry = new RectangleGeometry(new Rect(0.5, 0.5, Width - 1, Height - 1));
|
||||
}
|
||||
|
||||
private void CreateCircleGeometry()
|
||||
{
|
||||
DisplayGeometry = new EllipseGeometry(new Rect(0.5, 0.5, Width - 1, Height - 1));
|
||||
}
|
||||
|
||||
private void CreateKeyCapGeometry()
|
||||
{
|
||||
DisplayGeometry = new RectangleGeometry(new Rect(1, 1, Width - 2, Height - 2), 1.6, 1.6);
|
||||
}
|
||||
|
||||
private void CreateCustomGeometry(double deflateAmount)
|
||||
{
|
||||
try
|
||||
{
|
||||
DisplayGeometry = Geometry.Combine(
|
||||
Geometry.Empty,
|
||||
Geometry.Parse(Led.RgbLed.ShapeData),
|
||||
GeometryCombineMode.Union,
|
||||
new TransformGroup
|
||||
{
|
||||
Children = new TransformCollection
|
||||
{
|
||||
new ScaleTransform(Width - deflateAmount, Height - deflateAmount),
|
||||
new TranslateTransform(deflateAmount / 2, deflateAmount / 2)
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
CreateRectangleGeometry();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -15,7 +15,7 @@ using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.Visualization
|
||||
{
|
||||
public class ProfileViewModel : ProfileEditorPanelViewModel, IHandle<MainWindowFocusChangedEvent>, IHandle<MainWindowKeyEvent>
|
||||
public class ProfileViewModel : Screen, IProfileEditorPanelViewModel, IHandle<MainWindowFocusChangedEvent>, IHandle<MainWindowKeyEvent>
|
||||
{
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private readonly IProfileLayerVmFactory _profileLayerVmFactory;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<controls:MaterialWindow x:Class="Artemis.UI.Screens.ProfileEditor.LayerBrushSettingsWindowView"
|
||||
<controls:MaterialWindow x:Class="Artemis.UI.Screens.ProfileEditor.Windows.LayerBrushSettingsWindowView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
@ -2,7 +2,7 @@
|
||||
using Artemis.Core.LayerBrushes;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor
|
||||
namespace Artemis.UI.Screens.ProfileEditor.Windows
|
||||
{
|
||||
public class LayerBrushSettingsWindowViewModel : Conductor<BrushConfigurationViewModel>
|
||||
{
|
||||
@ -1,4 +1,4 @@
|
||||
<controls:MaterialWindow x:Class="Artemis.UI.Screens.ProfileEditor.LayerEffectSettingsWindowView"
|
||||
<controls:MaterialWindow x:Class="Artemis.UI.Screens.ProfileEditor.Windows.LayerEffectSettingsWindowView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
@ -2,7 +2,7 @@
|
||||
using Artemis.Core.LayerEffects;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor
|
||||
namespace Artemis.UI.Screens.ProfileEditor.Windows
|
||||
{
|
||||
public class LayerEffectSettingsWindowViewModel : Conductor<EffectConfigurationViewModel>
|
||||
{
|
||||
Loading…
x
Reference in New Issue
Block a user