1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-31 17:53:32 +00:00

Core - Streamlined public parts of profile creation

UI - Started adjusting the VMs for the layer refactor
This commit is contained in:
Robert 2020-09-10 19:56:39 +02:00 committed by SpoinkyNL
parent 1de6fefc2a
commit c07ea09c9d
58 changed files with 694 additions and 1196 deletions

View File

@ -61,7 +61,7 @@ namespace Artemis.Core
/// <returns></returns> /// <returns></returns>
public abstract bool EvaluateObject(object target); public abstract bool EvaluateObject(object target);
internal abstract void ApplyToEntity(); internal abstract void Save();
internal abstract DisplayConditionPartEntity GetEntity(); internal abstract DisplayConditionPartEntity GetEntity();
#region IDisposable #region IDisposable

View File

@ -105,14 +105,14 @@ namespace Artemis.Core
}; };
} }
internal override void ApplyToEntity() internal override void Save()
{ {
Entity.BooleanOperator = (int) BooleanOperator; Entity.BooleanOperator = (int) BooleanOperator;
Entity.Children.Clear(); Entity.Children.Clear();
Entity.Children.AddRange(Children.Select(c => c.GetEntity())); Entity.Children.AddRange(Children.Select(c => c.GetEntity()));
foreach (var child in Children) foreach (var child in Children)
child.ApplyToEntity(); child.Save();
} }
internal override DisplayConditionPartEntity GetEntity() internal override DisplayConditionPartEntity GetEntity()

View File

@ -116,7 +116,7 @@ namespace Artemis.Core
CompiledListAccessor = lambda.Compile(); CompiledListAccessor = lambda.Compile();
} }
internal override void ApplyToEntity() internal override void Save()
{ {
// Target list // Target list
if (ListDataModel != null) if (ListDataModel != null)
@ -132,7 +132,7 @@ namespace Artemis.Core
Entity.Children.Clear(); Entity.Children.Clear();
Entity.Children.AddRange(Children.Select(c => c.GetEntity())); Entity.Children.AddRange(Children.Select(c => c.GetEntity()));
foreach (var child in Children) foreach (var child in Children)
child.ApplyToEntity(); child.Save();
} }
internal override DisplayConditionPartEntity GetEntity() internal override DisplayConditionPartEntity GetEntity()

View File

@ -190,22 +190,31 @@ namespace Artemis.Core
return result; return result;
} }
internal override void ApplyToEntity() internal override void Save()
{ {
Entity.PredicateType = (int) PredicateType; Entity.PredicateType = (int) PredicateType;
Entity.ListDataModelGuid = ListDataModel?.PluginInfo?.Guid; if (ListDataModel != null)
Entity.ListPropertyPath = ListPropertyPath; {
Entity.ListDataModelGuid = ListDataModel.PluginInfo.Guid;
Entity.ListPropertyPath = ListPropertyPath;
}
Entity.LeftPropertyPath = LeftPropertyPath; Entity.LeftPropertyPath = LeftPropertyPath;
Entity.RightPropertyPath = RightPropertyPath; Entity.RightPropertyPath = RightPropertyPath;
Entity.RightStaticValue = JsonConvert.SerializeObject(RightStaticValue); Entity.RightStaticValue = JsonConvert.SerializeObject(RightStaticValue);
Entity.OperatorPluginGuid = Operator?.PluginInfo?.Guid; if (Operator != null)
Entity.OperatorType = Operator?.GetType().Name; {
Entity.OperatorPluginGuid = Operator.PluginInfo.Guid;
Entity.OperatorType = Operator.GetType().Name;
}
} }
internal void Initialize() private void Initialize()
{ {
ConditionOperatorStore.ConditionOperatorAdded += ConditionOperatorStoreOnConditionOperatorAdded;
ConditionOperatorStore.ConditionOperatorRemoved += ConditionOperatorStoreOnConditionOperatorRemoved;
// Left side // Left side
if (Entity.LeftPropertyPath != null && ListContainsInnerPath(Entity.LeftPropertyPath)) if (Entity.LeftPropertyPath != null && ListContainsInnerPath(Entity.LeftPropertyPath))
UpdateLeftSide(Entity.LeftPropertyPath); UpdateLeftSide(Entity.LeftPropertyPath);
@ -222,7 +231,7 @@ namespace Artemis.Core
if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightPropertyPath != null) if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightPropertyPath != null)
{ {
if (ListContainsInnerPath(Entity.RightPropertyPath)) if (ListContainsInnerPath(Entity.RightPropertyPath))
UpdateLeftSide(Entity.LeftPropertyPath); UpdateRightSideDynamic(Entity.RightPropertyPath);
} }
// Right side static // Right side static
else if (PredicateType == ProfileRightSideType.Static && Entity.RightStaticValue != null) else if (PredicateType == ProfileRightSideType.Static && Entity.RightStaticValue != null)
@ -384,5 +393,37 @@ namespace Artemis.Core
Expression.Property 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
} }
} }

View File

@ -232,7 +232,7 @@ namespace Artemis.Core
return $"[Static] {LeftPropertyPath} {Operator.Description} {RightStaticValue}"; return $"[Static] {LeftPropertyPath} {Operator.Description} {RightStaticValue}";
} }
internal override void ApplyToEntity() internal override void Save()
{ {
Entity.PredicateType = (int) PredicateType; Entity.PredicateType = (int) PredicateType;
Entity.LeftDataModelGuid = LeftDataModel?.PluginInfo?.Guid; Entity.LeftDataModelGuid = LeftDataModel?.PluginInfo?.Guid;
@ -250,6 +250,8 @@ namespace Artemis.Core
{ {
DataModelStore.DataModelAdded += DataModelStoreOnDataModelAdded; DataModelStore.DataModelAdded += DataModelStoreOnDataModelAdded;
DataModelStore.DataModelRemoved += DataModelStoreOnDataModelRemoved; DataModelStore.DataModelRemoved += DataModelStoreOnDataModelRemoved;
ConditionOperatorStore.ConditionOperatorAdded += ConditionOperatorStoreOnConditionOperatorAdded;
ConditionOperatorStore.ConditionOperatorRemoved += ConditionOperatorStoreOnConditionOperatorRemoved;
// Left side // Left side
if (Entity.LeftDataModelGuid != null) 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 #region Event handlers
private void DataModelStoreOnDataModelAdded(object sender, DataModelStoreEvent e) 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 #endregion
/// <inheritdoc /> /// <inheritdoc />
@ -461,6 +467,8 @@ namespace Artemis.Core
{ {
DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded; DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded;
DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved; DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved;
ConditionOperatorStore.ConditionOperatorAdded -= ConditionOperatorStoreOnConditionOperatorAdded;
ConditionOperatorStore.ConditionOperatorRemoved -= ConditionOperatorStoreOnConditionOperatorRemoved;
base.Dispose(disposing); base.Dispose(disposing);
} }

View File

@ -8,24 +8,35 @@ using SkiaSharp;
namespace Artemis.Core namespace Artemis.Core
{ {
/// <summary>
/// Represents a folder in a <see cref="Profile" />
/// </summary>
public sealed class Folder : RenderProfileElement public sealed class Folder : RenderProfileElement
{ {
private SKBitmap _folderBitmap; 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(); FolderEntity = new FolderEntity();
EntityId = Guid.NewGuid(); EntityId = Guid.NewGuid();
Profile = profile; Parent = parent ?? throw new ArgumentNullException(nameof(parent));
Parent = parent; Profile = Parent.Profile;
Name = name; Name = name;
Enabled = true; Enabled = true;
DisplayContinuously = true; DisplayContinuously = true;
_layerEffects = new List<BaseLayerEffect>(); _layerEffects = new List<BaseLayerEffect>();
_expandedPropertyGroups = new List<string>(); _expandedPropertyGroups = new List<string>();
ApplyRenderElementDefaults(); ApplyRenderElementDefaults();
Parent.AddChild(this);
} }
internal Folder(Profile profile, ProfileElement parent, FolderEntity folderEntity) internal Folder(Profile profile, ProfileElement parent, FolderEntity folderEntity)
@ -182,22 +193,7 @@ namespace Artemis.Core
canvas.Restore(); 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 /> /// <inheritdoc />
public override void AddChild(ProfileElement child, int? order = null) public override void AddChild(ProfileElement child, int? order = null)
{ {
@ -296,7 +292,7 @@ namespace Artemis.Core
// Conditions // Conditions
RenderElementEntity.RootDisplayCondition = DisplayConditionGroup?.Entity; RenderElementEntity.RootDisplayCondition = DisplayConditionGroup?.Entity;
DisplayConditionGroup?.ApplyToEntity(); DisplayConditionGroup?.Save();
SaveRenderElement(); SaveRenderElement();
} }

View File

@ -11,8 +11,7 @@ using SkiaSharp;
namespace Artemis.Core namespace Artemis.Core
{ {
/// <summary> /// <summary>
/// Represents a layer on a profile. To create new layers use the <see cref="RenderElementService" /> by injecting /// Represents a layer in a <see cref="Profile" />
/// <see cref="IRenderElementService" /> into your code
/// </summary> /// </summary>
public sealed class Layer : RenderProfileElement public sealed class Layer : RenderProfileElement
{ {
@ -23,13 +22,19 @@ namespace Artemis.Core
private List<ArtemisLed> _leds; private List<ArtemisLed> _leds;
private LayerTransformProperties _transform; 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(); LayerEntity = new LayerEntity();
EntityId = Guid.NewGuid(); EntityId = Guid.NewGuid();
Profile = profile; Parent = parent ?? throw new ArgumentNullException(nameof(parent));
Parent = parent; Profile = Parent.Profile;
Name = name; Name = name;
Enabled = true; Enabled = true;
DisplayContinuously = true; DisplayContinuously = true;
@ -40,10 +45,10 @@ namespace Artemis.Core
_leds = new List<ArtemisLed>(); _leds = new List<ArtemisLed>();
_expandedPropertyGroups = new List<string>(); _expandedPropertyGroups = new List<string>();
InitializeDefaultGroups(); Initialize();
parent.AddChild(this);
ApplyRenderElementDefaults(); ApplyRenderElementDefaults();
Parent.AddChild(this);
} }
internal Layer(Profile profile, ProfileElement parent, LayerEntity layerEntity) internal Layer(Profile profile, ProfileElement parent, LayerEntity layerEntity)
@ -58,8 +63,7 @@ namespace Artemis.Core
_leds = new List<ArtemisLed>(); _leds = new List<ArtemisLed>();
_expandedPropertyGroups = new List<string>(); _expandedPropertyGroups = new List<string>();
InitializeDefaultGroups(); Initialize();
Load(); Load();
} }
@ -114,6 +118,8 @@ namespace Artemis.Core
return $"[Layer] {nameof(Name)}: {Name}, {nameof(Order)}: {Order}"; return $"[Layer] {nameof(Name)}: {Name}, {nameof(Order)}: {Order}";
} }
#region IDisposable
/// <inheritdoc /> /// <inheritdoc />
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
@ -133,8 +139,13 @@ namespace Artemis.Core
_transform?.Dispose(); _transform?.Dispose();
} }
private void InitializeDefaultGroups() #endregion
private void Initialize()
{ {
LayerBrushStore.LayerBrushAdded += LayerBrushStoreOnLayerBrushAdded;
LayerBrushStore.LayerBrushRemoved += LayerBrushStoreOnLayerBrushRemoved;
// Layers have two hardcoded property groups, instantiate them // Layers have two hardcoded property groups, instantiate them
General.Initialize(this, "General.", Constants.CorePluginInfo); General.Initialize(this, "General.", Constants.CorePluginInfo);
Transform.Initialize(this, "Transform.", Constants.CorePluginInfo); Transform.Initialize(this, "Transform.", Constants.CorePluginInfo);
@ -191,7 +202,7 @@ namespace Artemis.Core
// Conditions // Conditions
RenderElementEntity.RootDisplayCondition = DisplayConditionGroup?.Entity; RenderElementEntity.RootDisplayCondition = DisplayConditionGroup?.Entity;
DisplayConditionGroup?.ApplyToEntity(); DisplayConditionGroup?.Save();
SaveRenderElement(); SaveRenderElement();
} }
@ -711,6 +722,27 @@ namespace Artemis.Core
#endregion #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 #region Events
public event EventHandler RenderPropertiesUpdated; public event EventHandler RenderPropertiesUpdated;

View File

@ -16,17 +16,19 @@ namespace Artemis.Core
ProfileEntity = new ProfileEntity(); ProfileEntity = new ProfileEntity();
EntityId = Guid.NewGuid(); EntityId = Guid.NewGuid();
Profile = this;
Module = module; Module = module;
Name = name; Name = name;
UndoStack = new Stack<string>(); UndoStack = new Stack<string>();
RedoStack = new Stack<string>(); RedoStack = new Stack<string>();
AddChild(new Folder(this, this, "Root folder")); var _ = new Folder(this, "Root folder");
Save(); Save();
} }
internal Profile(ProfileModule module, ProfileEntity profileEntity) internal Profile(ProfileModule module, ProfileEntity profileEntity)
{ {
Profile = this;
ProfileEntity = profileEntity; ProfileEntity = profileEntity;
EntityId = profileEntity.Id; EntityId = profileEntity.Id;
@ -103,7 +105,9 @@ namespace Artemis.Core
// Populate the profile starting at the root, the rest is populated recursively // Populate the profile starting at the root, the rest is populated recursively
var rootFolder = ProfileEntity.Folders.FirstOrDefault(f => f.ParentId == EntityId); var rootFolder = ProfileEntity.Folders.FirstOrDefault(f => f.ParentId == EntityId);
if (rootFolder == null) if (rootFolder == null)
AddChild(new Folder(this, this, "Root folder")); {
var _ = new Folder(this, "Root folder");
}
else else
AddChild(new Folder(this, this, rootFolder)); AddChild(new Folder(this, this, rootFolder));
} }

View File

@ -110,9 +110,12 @@ namespace Artemis.Core
{ {
if (_disposed) if (_disposed)
throw new ObjectDisposedException(GetType().Name); throw new ObjectDisposedException(GetType().Name);
lock (ChildrenList) lock (ChildrenList)
{ {
if (ChildrenList.Contains(child))
return;
// Add to the end of the list // Add to the end of the list
if (order == null) if (order == null)
{ {

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using Artemis.Core.LayerEffects; using Artemis.Core.LayerEffects;
using Artemis.Core.LayerEffects.Placeholder;
using Artemis.Core.Properties; using Artemis.Core.Properties;
using Artemis.Storage.Entities.Profile; using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile.Abstract; using Artemis.Storage.Entities.Profile.Abstract;
@ -49,7 +50,7 @@ namespace Artemis.Core
{ {
Id = layerEffect.EntityId, Id = layerEffect.EntityId,
PluginGuid = layerEffect.PluginInfo.Guid, PluginGuid = layerEffect.PluginInfo.Guid,
EffectType = layerEffect.GetType().Name, EffectType = layerEffect.GetEffectTypeName(),
Name = layerEffect.Name, Name = layerEffect.Name,
Enabled = layerEffect.Enabled, Enabled = layerEffect.Enabled,
HasBeenRenamed = layerEffect.HasBeenRenamed, HasBeenRenamed = layerEffect.HasBeenRenamed,
@ -268,11 +269,29 @@ namespace Artemis.Core
{ {
foreach (var layerEffectEntity in RenderElementEntity.LayerEffects) 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; continue;
var descriptor = LayerEffectStore.Get(layerEffectEntity.PluginGuid, layerEffectEntity.EffectType)?.LayerEffectDescriptor; 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(); 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 #endregion

View File

@ -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 // Not only is this needed to initialize properties on the layer effects, it also prevents implementing anything
// but LayerEffect<T> outside the core // but LayerEffect<T> outside the core
internal abstract void Initialize(); internal abstract void Initialize();
internal virtual string GetEffectTypeName() => GetType().Name;
} }
} }

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Linq; using System.Linq;
using Artemis.Core.LayerBrushes;
using Artemis.Storage.Entities.Profile; using Artemis.Storage.Entities.Profile;
using Ninject; using Ninject;
@ -51,6 +50,11 @@ namespace Artemis.Core.LayerEffects
/// </summary> /// </summary>
internal IKernel Kernel { get; set; } 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> /// <summary>
/// Creates an instance of the described effect and applies it to the render element /// Creates an instance of the described effect and applies it to the render element
/// </summary> /// </summary>
@ -60,6 +64,12 @@ namespace Artemis.Core.LayerEffects
if (renderElement.LayerEffects.Any(e => e.EntityId == entity.Id)) if (renderElement.LayerEffects.Any(e => e.EntityId == entity.Id))
return; return;
if (IsPlaceHolder)
{
CreatePlaceHolderInstance(renderElement, entity);
return;
}
var effect = (BaseLayerEffect) Kernel.Get(LayerEffectType); var effect = (BaseLayerEffect) Kernel.Get(LayerEffectType);
effect.ProfileElement = renderElement; effect.ProfileElement = renderElement;
effect.EntityId = entity.Id; effect.EntityId = entity.Id;
@ -73,5 +83,11 @@ namespace Artemis.Core.LayerEffects
renderElement.ActivateLayerEffect(effect); renderElement.ActivateLayerEffect(effect);
} }
private void CreatePlaceHolderInstance(RenderProfileElement renderElement, LayerEffectEntity entity)
{
var effect = new PlaceholderLayerEffect(entity) {ProfileElement = renderElement, Descriptor = this};
renderElement.ActivateLayerEffect(effect);
}
} }
} }

View File

@ -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()
{
}
}
}

View File

@ -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;
}
}
}

View File

@ -51,7 +51,8 @@ namespace Artemis.Core
{ {
lock (Registrations) 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);
} }
} }

View File

@ -10,7 +10,7 @@ namespace Artemis.UI.Shared.Services
{ {
Profile SelectedProfile { get; } Profile SelectedProfile { get; }
RenderProfileElement SelectedProfileElement { get; } RenderProfileElement SelectedProfileElement { get; }
BaseLayerProperty SelectedDataBinding { get; } ILayerProperty SelectedDataBinding { get; }
TimeSpan CurrentTime { get; set; } TimeSpan CurrentTime { get; set; }
int PixelsPerSecond { get; set; } int PixelsPerSecond { get; set; }
IReadOnlyList<PropertyInputRegistration> RegisteredPropertyEditors { get; } IReadOnlyList<PropertyInputRegistration> RegisteredPropertyEditors { get; }
@ -19,7 +19,7 @@ namespace Artemis.UI.Shared.Services
void UpdateSelectedProfile(); void UpdateSelectedProfile();
void ChangeSelectedProfileElement(RenderProfileElement profileElement); void ChangeSelectedProfileElement(RenderProfileElement profileElement);
void UpdateSelectedProfileElement(); void UpdateSelectedProfileElement();
void ChangeSelectedDataBinding(BaseLayerProperty layerProperty); void ChangeSelectedDataBinding(ILayerProperty layerProperty);
void UpdateProfilePreview(); void UpdateProfilePreview();
bool UndoUpdateProfile(); bool UndoUpdateProfile();
bool RedoUpdateProfile(); bool RedoUpdateProfile();
@ -88,5 +88,10 @@ namespace Artemis.UI.Shared.Services
/// <param name="excludedKeyframe">A keyframe to exclude during keyframe snapping</param> /// <param name="excludedKeyframe">A keyframe to exclude during keyframe snapping</param>
/// <returns></returns> /// <returns></returns>
TimeSpan SnapToTimeline(TimeSpan time, TimeSpan tolerance, bool snapToSegments, bool snapToCurrentTime, bool snapToKeyframes, BaseLayerPropertyKeyframe excludedKeyframe = null); 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);
} }
} }

View File

@ -5,6 +5,7 @@ using Artemis.Core;
using Artemis.Core.Modules; using Artemis.Core.Modules;
using Artemis.Core.Services; using Artemis.Core.Services;
using Ninject; using Ninject;
using Ninject.Parameters;
using Serilog; using Serilog;
using Stylet; using Stylet;
@ -269,6 +270,16 @@ namespace Artemis.UI.Shared.Services
return time; 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() public ProfileModule GetCurrentModule()
{ {
return SelectedProfile?.Module; return SelectedProfile?.Module;

View File

@ -1,13 +1,10 @@
using System.Reflection; using Artemis.Core;
using Artemis.Core;
using Artemis.Core.Modules; using Artemis.Core.Modules;
using Artemis.UI.Screens.Modules; using Artemis.UI.Screens.Modules;
using Artemis.UI.Screens.Modules.Tabs; using Artemis.UI.Screens.Modules.Tabs;
using Artemis.UI.Screens.ProfileEditor; using Artemis.UI.Screens.ProfileEditor;
using Artemis.UI.Screens.ProfileEditor.DisplayConditions; 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;
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Abstract;
using Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings; using Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings;
using Artemis.UI.Screens.ProfileEditor.LayerProperties.LayerEffects; using Artemis.UI.Screens.ProfileEditor.LayerProperties.LayerEffects;
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline; 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.Devices;
using Artemis.UI.Screens.Settings.Tabs.Plugins; using Artemis.UI.Screens.Settings.Tabs.Plugins;
using Stylet; using Stylet;
using Module = Artemis.Core.Modules.Module;
namespace Artemis.UI.Ninject.Factories namespace Artemis.UI.Ninject.Factories
{ {
@ -46,15 +42,10 @@ namespace Artemis.UI.Ninject.Factories
DeviceDebugViewModel Create(ArtemisDevice device); DeviceDebugViewModel Create(ArtemisDevice device);
} }
public interface IFolderVmFactory : IVmFactory public interface IProfileTreeVmFactory : IVmFactory
{ {
FolderViewModel Create(ProfileElement folder); FolderViewModel FolderViewModel(ProfileElement folder);
FolderViewModel Create(TreeItemViewModel parent, ProfileElement folder); LayerViewModel LayerViewModel(ProfileElement layer);
}
public interface ILayerVmFactory : IVmFactory
{
LayerViewModel Create(TreeItemViewModel parent, ProfileElement folder);
} }
public interface IProfileLayerVmFactory : IVmFactory public interface IProfileLayerVmFactory : IVmFactory
@ -72,10 +63,10 @@ namespace Artemis.UI.Ninject.Factories
public interface IDisplayConditionsVmFactory : IVmFactory public interface IDisplayConditionsVmFactory : IVmFactory
{ {
DisplayConditionGroupViewModel DisplayConditionGroupViewModel(DisplayConditionGroup displayConditionGroup, DisplayConditionViewModel parent, bool isListGroup); DisplayConditionGroupViewModel DisplayConditionGroupViewModel(DisplayConditionGroup displayConditionGroup, bool isListGroup);
DisplayConditionListViewModel DisplayConditionListViewModel(DisplayConditionList displayConditionList, DisplayConditionViewModel parent); DisplayConditionListViewModel DisplayConditionListViewModel(DisplayConditionList displayConditionList);
DisplayConditionPredicateViewModel DisplayConditionPredicateViewModel(DisplayConditionPredicate displayConditionPredicate, DisplayConditionViewModel parent); DisplayConditionPredicateViewModel DisplayConditionPredicateViewModel(DisplayConditionPredicate displayConditionPredicate);
DisplayConditionListPredicateViewModel DisplayConditionListPredicateViewModel(DisplayConditionListPredicate displayConditionListPredicate, DisplayConditionViewModel parent); DisplayConditionListPredicateViewModel DisplayConditionListPredicateViewModel(DisplayConditionListPredicate displayConditionListPredicate);
} }
public interface IDataBindingsVmFactory : IVmFactory public interface IDataBindingsVmFactory : IVmFactory
@ -87,11 +78,13 @@ namespace Artemis.UI.Ninject.Factories
public interface ILayerPropertyVmFactory : IVmFactory 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); LayerPropertyGroupViewModel LayerPropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, PropertyGroupDescriptionAttribute propertyGroupDescription);
TreeViewModel TreeViewModel(LayerPropertiesViewModel layerPropertiesViewModel, BindableCollection<LayerPropertyGroupViewModel> layerPropertyGroups); TreeViewModel TreeViewModel(LayerPropertiesViewModel layerPropertiesViewModel, BindableCollection<LayerPropertyGroupViewModel> layerPropertyGroups);
EffectsViewModel EffectsViewModel(LayerPropertiesViewModel layerPropertiesViewModel); EffectsViewModel EffectsViewModel(LayerPropertiesViewModel layerPropertiesViewModel);
TimelineViewModel TimelineViewModel(LayerPropertiesViewModel layerPropertiesViewModel, BindableCollection<LayerPropertyGroupViewModel> layerPropertyGroups); TimelineViewModel TimelineViewModel(LayerPropertiesViewModel layerPropertiesViewModel, BindableCollection<LayerPropertyGroupViewModel> layerPropertyGroups);
TreePropertyGroupViewModel TreePropertyGroupViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel);
TimelinePropertyGroupViewModel TimelinePropertyGroupViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel);
} }
} }

View File

@ -53,7 +53,7 @@ namespace Artemis.UI.Ninject
{ {
x.FromThisAssembly() x.FromThisAssembly()
.SelectAllClasses() .SelectAllClasses()
.InheritedFrom<ProfileEditorPanelViewModel>() .InheritedFrom<IProfileEditorPanelViewModel>()
.BindAllBaseClasses(); .BindAllBaseClasses();
}); });

View File

@ -1,44 +1,23 @@
using System; using Artemis.Core;
using Artemis.Core;
using Stylet; using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract 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; Model = model;
Parent = parent;
Children = new BindableCollection<DisplayConditionViewModel>();
} }
public DisplayConditionPart Model { get; } 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 abstract void Update();
public virtual void Delete() public virtual void Delete()
{ {
Model.Parent.RemoveChild(Model); Model.Parent.RemoveChild(Model);
Parent.Update(); ((DisplayConditionViewModel) Parent).Update();
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
}
} }
} }
} }

View File

@ -124,7 +124,7 @@
<materialDesign:PackIcon Kind="Add" Width="18" Height="18" /> <materialDesign:PackIcon Kind="Add" Width="18" Height="18" />
</Button> </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> <ItemsControl.ItemTemplate>
<DataTemplate> <DataTemplate>
<materialDesign:TransitioningContent> <materialDesign:TransitioningContent>

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Ninject.Factories; using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract; using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract;
@ -11,21 +10,24 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
{ {
public class DisplayConditionGroupViewModel : DisplayConditionViewModel, IViewAware public class DisplayConditionGroupViewModel : DisplayConditionViewModel
{ {
private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory; private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory;
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private bool _isInitialized; private bool _isInitialized;
private bool _isRootGroup; private bool _isRootGroup;
public DisplayConditionGroupViewModel(DisplayConditionGroup displayConditionGroup, DisplayConditionViewModel parent, bool isListGroup, public DisplayConditionGroupViewModel(DisplayConditionGroup displayConditionGroup,
IProfileEditorService profileEditorService, IDisplayConditionsVmFactory displayConditionsVmFactory) : base(displayConditionGroup, parent) bool isListGroup,
IProfileEditorService profileEditorService,
IDisplayConditionsVmFactory displayConditionsVmFactory)
: base(displayConditionGroup)
{ {
IsListGroup = isListGroup; IsListGroup = isListGroup;
_profileEditorService = profileEditorService; _profileEditorService = profileEditorService;
_displayConditionsVmFactory = displayConditionsVmFactory; _displayConditionsVmFactory = displayConditionsVmFactory;
Children.CollectionChanged += (sender, args) => NotifyOfPropertyChange(nameof(DisplayBooleanOperator)); Items.CollectionChanged += (sender, args) => NotifyOfPropertyChange(nameof(DisplayBooleanOperator));
Execute.PostToUIThread(async () => Execute.PostToUIThread(async () =>
{ {
@ -50,16 +52,9 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
set => SetAndNotify(ref _isInitialized, value); set => SetAndNotify(ref _isInitialized, value);
} }
public bool DisplayBooleanOperator => Children.Count > 1; public bool DisplayBooleanOperator => Items.Count > 1;
public string SelectedBooleanOperator => DisplayConditionGroup.BooleanOperator.Humanize(); public string SelectedBooleanOperator => DisplayConditionGroup.BooleanOperator.Humanize();
public void AttachView(UIElement view)
{
View = view;
}
public UIElement View { get; set; }
public void SelectBooleanOperator(string type) public void SelectBooleanOperator(string type)
{ {
var enumValue = Enum.Parse<BooleanOperator>(type); var enumValue = Enum.Parse<BooleanOperator>(type);
@ -105,39 +100,36 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
NotifyOfPropertyChange(nameof(SelectedBooleanOperator)); NotifyOfPropertyChange(nameof(SelectedBooleanOperator));
// Remove VMs of effects no longer applied on the layer // 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 // Using RemoveRange breaks our lovely animations
foreach (var displayConditionViewModel in toRemove) foreach (var displayConditionViewModel in toRemove)
{ CloseItem(displayConditionViewModel);
Children.Remove(displayConditionViewModel);
displayConditionViewModel.Dispose();
}
foreach (var childModel in Model.Children) foreach (var childModel in Model.Children)
{ {
if (Children.Any(c => c.Model == childModel)) if (Items.Any(c => c.Model == childModel))
continue; continue;
switch (childModel) switch (childModel)
{ {
case DisplayConditionGroup displayConditionGroup: case DisplayConditionGroup displayConditionGroup:
Children.Add(_displayConditionsVmFactory.DisplayConditionGroupViewModel(displayConditionGroup, this, IsListGroup)); ActivateItem(_displayConditionsVmFactory.DisplayConditionGroupViewModel(displayConditionGroup, IsListGroup));
break; break;
case DisplayConditionList displayConditionListPredicate: case DisplayConditionList displayConditionListPredicate:
Children.Add(_displayConditionsVmFactory.DisplayConditionListViewModel(displayConditionListPredicate, this)); ActivateItem(_displayConditionsVmFactory.DisplayConditionListViewModel(displayConditionListPredicate));
break; break;
case DisplayConditionPredicate displayConditionPredicate: case DisplayConditionPredicate displayConditionPredicate:
if (!IsListGroup) if (!IsListGroup)
Children.Add(_displayConditionsVmFactory.DisplayConditionPredicateViewModel(displayConditionPredicate, this)); ActivateItem(_displayConditionsVmFactory.DisplayConditionPredicateViewModel(displayConditionPredicate));
break; break;
case DisplayConditionListPredicate displayConditionListPredicate: case DisplayConditionListPredicate displayConditionListPredicate:
if (IsListGroup) if (IsListGroup)
Children.Add(_displayConditionsVmFactory.DisplayConditionListPredicateViewModel(displayConditionListPredicate, this)); ActivateItem(_displayConditionsVmFactory.DisplayConditionListPredicateViewModel(displayConditionListPredicate));
break; break;
} }
} }
foreach (var childViewModel in Children) foreach (var childViewModel in Items)
childViewModel.Update(); childViewModel.Update();
} }
} }

View File

@ -17,9 +17,9 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions 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 IDataModelUIService _dataModelUIService;
private readonly IEventAggregator _eventAggregator; private readonly IEventAggregator _eventAggregator;
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
@ -39,16 +39,15 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
public DisplayConditionListPredicateViewModel( public DisplayConditionListPredicateViewModel(
DisplayConditionListPredicate displayConditionListPredicate, DisplayConditionListPredicate displayConditionListPredicate,
DisplayConditionViewModel parent,
IProfileEditorService profileEditorService, IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService, IDataModelUIService dataModelUIService,
IDataModelService dataModelService, IConditionOperatorService conditionOperatorService,
ISettingsService settingsService, ISettingsService settingsService,
IEventAggregator eventAggregator) : base(displayConditionListPredicate, parent) IEventAggregator eventAggregator) : base(displayConditionListPredicate)
{ {
_profileEditorService = profileEditorService; _profileEditorService = profileEditorService;
_dataModelUIService = dataModelUIService; _dataModelUIService = dataModelUIService;
_dataModelService = dataModelService; _conditionOperatorService = conditionOperatorService;
_eventAggregator = eventAggregator; _eventAggregator = eventAggregator;
_updateTimer = new Timer(500); _updateTimer = new Timer(500);
_supportedInputTypes = new List<Type>(); _supportedInputTypes = new List<Type>();
@ -206,7 +205,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
// Get the supported operators // Get the supported operators
Operators.Clear(); Operators.Clear();
Operators.AddRange(_dataModelService.GetConditionOperatorsForType(leftSideType)); Operators.AddRange(_conditionOperatorService.GetConditionOperatorsForType(leftSideType));
if (DisplayConditionListPredicate.Operator == null) if (DisplayConditionListPredicate.Operator == null)
DisplayConditionListPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType))); DisplayConditionListPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType)));
SelectedOperator = DisplayConditionListPredicate.Operator; SelectedOperator = DisplayConditionListPredicate.Operator;
@ -277,12 +276,6 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
_eventAggregator.Subscribe(this); _eventAggregator.Subscribe(this);
} }
protected override void Dispose(bool disposing)
{
_updateTimer.Stop();
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
}
private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e) private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
{ {
if (LeftSideDataModelOpen) if (LeftSideDataModelOpen)
@ -353,5 +346,11 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
SelectedOperator = displayConditionOperator; SelectedOperator = displayConditionOperator;
ApplyOperator(); ApplyOperator();
} }
public void Dispose()
{
_updateTimer.Dispose();
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
}
} }
} }

View File

@ -105,7 +105,7 @@
</Button.ContextMenu> </Button.ContextMenu>
</Button> </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> <ItemsControl.ItemTemplate>
<DataTemplate> <DataTemplate>
<materialDesign:TransitioningContent> <materialDesign:TransitioningContent>

View File

@ -14,7 +14,7 @@ using Humanizer;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
{ {
public class DisplayConditionListViewModel : DisplayConditionViewModel public class DisplayConditionListViewModel : DisplayConditionViewModel, IDisposable
{ {
private readonly IDataModelUIService _dataModelUIService; private readonly IDataModelUIService _dataModelUIService;
private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory; private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory;
@ -30,7 +30,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
IProfileEditorService profileEditorService, IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService, IDataModelUIService dataModelUIService,
IDisplayConditionsVmFactory displayConditionsVmFactory, IDisplayConditionsVmFactory displayConditionsVmFactory,
ISettingsService settingsService) : base(displayConditionList, parent) ISettingsService settingsService) : base(displayConditionList)
{ {
_profileEditorService = profileEditorService; _profileEditorService = profileEditorService;
_dataModelUIService = dataModelUIService; _dataModelUIService = dataModelUIService;
@ -152,36 +152,27 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
TargetDataModel.ApplyTypeFilter(true, typeof(IList)); TargetDataModel.ApplyTypeFilter(true, typeof(IList));
// Remove VMs of effects no longer applied on the layer // 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 // Using RemoveRange breaks our lovely animations
foreach (var displayConditionViewModel in toRemove) foreach (var displayConditionViewModel in toRemove)
{ CloseItem(displayConditionViewModel);
Children.Remove(displayConditionViewModel);
displayConditionViewModel.Dispose();
}
foreach (var childModel in Model.Children) foreach (var childModel in Model.Children)
{ {
if (Children.Any(c => c.Model == childModel)) if (Items.Any(c => c.Model == childModel))
continue; continue;
if (!(childModel is DisplayConditionGroup displayConditionGroup)) if (!(childModel is DisplayConditionGroup displayConditionGroup))
continue; continue;
var viewModel = _displayConditionsVmFactory.DisplayConditionGroupViewModel(displayConditionGroup, this, true); var viewModel = _displayConditionsVmFactory.DisplayConditionGroupViewModel(displayConditionGroup, true);
viewModel.IsRootGroup = true; viewModel.IsRootGroup = true;
Children.Add(viewModel); ActivateItem(viewModel);
} }
foreach (var childViewModel in Children) foreach (var childViewModel in Items)
childViewModel.Update(); childViewModel.Update();
} }
protected override void Dispose(bool disposing)
{
_updateTimer.Stop();
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
}
private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e) private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
{ {
if (TargetDataModelOpen) if (TargetDataModelOpen)
@ -205,5 +196,11 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
SelectedListProperty = dataModelListViewModel; SelectedListProperty = dataModelListViewModel;
ApplyList(); ApplyList();
} }
public void Dispose()
{
_updateTimer.Dispose();
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
}
} }
} }

View File

@ -16,9 +16,9 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions 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 IDataModelUIService _dataModelUIService;
private readonly IEventAggregator _eventAggregator; private readonly IEventAggregator _eventAggregator;
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
@ -38,16 +38,15 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
public DisplayConditionPredicateViewModel( public DisplayConditionPredicateViewModel(
DisplayConditionPredicate displayConditionPredicate, DisplayConditionPredicate displayConditionPredicate,
DisplayConditionViewModel parent,
IProfileEditorService profileEditorService, IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService, IDataModelUIService dataModelUIService,
IDataModelService dataModelService, IConditionOperatorService conditionOperatorService,
ISettingsService settingsService, ISettingsService settingsService,
IEventAggregator eventAggregator) : base(displayConditionPredicate, parent) IEventAggregator eventAggregator) : base(displayConditionPredicate)
{ {
_profileEditorService = profileEditorService; _profileEditorService = profileEditorService;
_dataModelUIService = dataModelUIService; _dataModelUIService = dataModelUIService;
_dataModelService = dataModelService; _conditionOperatorService = conditionOperatorService;
_eventAggregator = eventAggregator; _eventAggregator = eventAggregator;
_updateTimer = new Timer(500); _updateTimer = new Timer(500);
_supportedInputTypes = new List<Type>(); _supportedInputTypes = new List<Type>();
@ -208,7 +207,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
// Get the supported operators // Get the supported operators
Operators.Clear(); Operators.Clear();
Operators.AddRange(_dataModelService.GetConditionOperatorsForType(leftSideType)); Operators.AddRange(_conditionOperatorService.GetConditionOperatorsForType(leftSideType));
if (DisplayConditionPredicate.Operator == null) if (DisplayConditionPredicate.Operator == null)
DisplayConditionPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType))); DisplayConditionPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType)));
SelectedOperator = DisplayConditionPredicate.Operator; SelectedOperator = DisplayConditionPredicate.Operator;
@ -278,13 +277,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
); );
_eventAggregator.Subscribe(this); _eventAggregator.Subscribe(this);
} }
protected override void Dispose(bool disposing)
{
_updateTimer.Stop();
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
}
private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e) private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
{ {
if (LeftSideDataModelOpen) if (LeftSideDataModelOpen)
@ -335,5 +328,11 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
SelectedOperator = displayConditionOperator; SelectedOperator = displayConditionOperator;
ApplyOperator(); ApplyOperator();
} }
public void Dispose()
{
_updateTimer.Dispose();
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
}
} }
} }

View File

@ -43,7 +43,7 @@
<Grid Grid.Row="2" Grid.Column="0"> <Grid Grid.Row="2" Grid.Column="0">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Background="{StaticResource MaterialDesignCardBackground}"> <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Background="{StaticResource MaterialDesignCardBackground}">
<ContentControl s:View.Model="{Binding RootGroup}" /> <ContentControl s:View.Model="{Binding ActiveItem}" />
</ScrollViewer> </ScrollViewer>
</Grid> </Grid>

View File

@ -3,17 +3,17 @@ using Artemis.Core;
using Artemis.UI.Ninject.Factories; using Artemis.UI.Ninject.Factories;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
{ {
public class DisplayConditionsViewModel : ProfileEditorPanelViewModel public class DisplayConditionsViewModel : Conductor<DisplayConditionGroupViewModel>, IProfileEditorPanelViewModel
{ {
private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory; private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory;
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private bool _alwaysFinishTimeline; private bool _alwaysFinishTimeline;
private bool _displayContinuously; private bool _displayContinuously;
private RenderProfileElement _renderProfileElement; private RenderProfileElement _renderProfileElement;
private DisplayConditionGroupViewModel _rootGroup;
private int _transitionerIndex; private int _transitionerIndex;
public DisplayConditionsViewModel(IProfileEditorService profileEditorService, IDisplayConditionsVmFactory displayConditionsVmFactory) public DisplayConditionsViewModel(IProfileEditorService profileEditorService, IDisplayConditionsVmFactory displayConditionsVmFactory)
@ -28,11 +28,6 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
set => SetAndNotify(ref _transitionerIndex, value); set => SetAndNotify(ref _transitionerIndex, value);
} }
public DisplayConditionGroupViewModel RootGroup
{
get => _rootGroup;
set => SetAndNotify(ref _rootGroup, value);
}
public RenderProfileElement RenderProfileElement public RenderProfileElement RenderProfileElement
{ {
@ -70,9 +65,6 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
protected override void OnDeactivate() protected override void OnDeactivate()
{ {
_profileEditorService.ProfileElementSelected -= ProfileEditorServiceOnProfileElementSelected; _profileEditorService.ProfileElementSelected -= ProfileEditorServiceOnProfileElementSelected;
RootGroup?.Dispose();
RootGroup = null;
} }
private void ProfileEditorServiceOnProfileElementSelected(object sender, RenderProfileElementEventArgs e) private void ProfileEditorServiceOnProfileElementSelected(object sender, RenderProfileElementEventArgs e)
@ -87,8 +79,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
if (e.RenderProfileElement == null) if (e.RenderProfileElement == null)
{ {
RootGroup?.Dispose(); ActiveItem = null;
RootGroup = null;
return; return;
} }
@ -96,14 +87,13 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
if (e.RenderProfileElement.DisplayConditionGroup == null) if (e.RenderProfileElement.DisplayConditionGroup == null)
e.RenderProfileElement.DisplayConditionGroup = new DisplayConditionGroup(null); e.RenderProfileElement.DisplayConditionGroup = new DisplayConditionGroup(null);
RootGroup?.Dispose(); ActiveItem = _displayConditionsVmFactory.DisplayConditionGroupViewModel(e.RenderProfileElement.DisplayConditionGroup, false);
RootGroup = _displayConditionsVmFactory.DisplayConditionGroupViewModel(e.RenderProfileElement.DisplayConditionGroup, null, false); ActiveItem.IsRootGroup = true;
RootGroup.IsRootGroup = true; ActiveItem.Update();
RootGroup.Update();
// Only show the intro to conditions once, and only if the layer has no conditions // Only show the intro to conditions once, and only if the layer has no conditions
if (TransitionerIndex != 1) if (TransitionerIndex != 1)
TransitionerIndex = RootGroup.Children.Any() ? 1 : 0; TransitionerIndex = ActiveItem.Items.Any() ? 1 : 0;
} }
} }
} }

View File

@ -2,7 +2,7 @@
namespace Artemis.UI.Screens.ProfileEditor namespace Artemis.UI.Screens.ProfileEditor
{ {
public class ProfileEditorPanelViewModel : Screen public interface IProfileEditorPanelViewModel : IScreen
{ {
} }
} }

View File

@ -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);
}
}

View File

@ -9,32 +9,22 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.LayerEffects namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.LayerEffects
{ {
public class EffectsViewModel : PropertyChangedBase public class EffectsViewModel : Conductor<LayerEffectDescriptor>.Collection.AllActive
{ {
private readonly IPluginService _pluginService; private readonly IPluginService _pluginService;
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private readonly IRenderElementService _renderElementService;
private BindableCollection<LayerEffectDescriptor> _layerEffectDescriptors;
private LayerEffectDescriptor _selectedLayerEffectDescriptor; private LayerEffectDescriptor _selectedLayerEffectDescriptor;
public EffectsViewModel(LayerPropertiesViewModel layerPropertiesViewModel, IPluginService pluginService, IRenderElementService renderElementService, IProfileEditorService profileEditorService) public EffectsViewModel(LayerPropertiesViewModel layerPropertiesViewModel, IPluginService pluginService, IProfileEditorService profileEditorService)
{ {
_pluginService = pluginService; _pluginService = pluginService;
_renderElementService = renderElementService;
_profileEditorService = profileEditorService; _profileEditorService = profileEditorService;
LayerPropertiesViewModel = layerPropertiesViewModel; LayerPropertiesViewModel = layerPropertiesViewModel;
LayerEffectDescriptors = new BindableCollection<LayerEffectDescriptor>();
PropertyChanged += HandleSelectedLayerEffectChanged; PropertyChanged += HandleSelectedLayerEffectChanged;
} }
public LayerPropertiesViewModel LayerPropertiesViewModel { get; } public LayerPropertiesViewModel LayerPropertiesViewModel { get; }
public bool HasLayerEffectDescriptors => LayerEffectDescriptors.Any(); public bool HasLayerEffectDescriptors => Items.Any();
public BindableCollection<LayerEffectDescriptor> LayerEffectDescriptors
{
get => _layerEffectDescriptors;
set => SetAndNotify(ref _layerEffectDescriptors, value);
}
public LayerEffectDescriptor SelectedLayerEffectDescriptor public LayerEffectDescriptor SelectedLayerEffectDescriptor
{ {
@ -46,15 +36,15 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.LayerEffects
{ {
var layerBrushProviders = _pluginService.GetPluginsOfType<LayerEffectProvider>(); var layerBrushProviders = _pluginService.GetPluginsOfType<LayerEffectProvider>();
var descriptors = layerBrushProviders.SelectMany(l => l.LayerEffectDescriptors).ToList(); var descriptors = layerBrushProviders.SelectMany(l => l.LayerEffectDescriptors).ToList();
LayerEffectDescriptors.AddRange(descriptors.Except(LayerEffectDescriptors)); Items.AddRange(descriptors.Except(Items));
LayerEffectDescriptors.RemoveRange(LayerEffectDescriptors.Except(descriptors)); Items.RemoveRange(Items.Except(descriptors));
// Sort by display name // Sort by display name
var index = 0; 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) if (Items.IndexOf(layerEffectDescriptor) != index)
LayerEffectDescriptors.Move(LayerEffectDescriptors.IndexOf(layerEffectDescriptor), index); ((BindableCollection<LayerEffectDescriptor>) Items).Move(Items.IndexOf(layerEffectDescriptor), index);
index++; index++;
} }
@ -78,7 +68,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.LayerEffects
Execute.PostToUIThread(async () => Execute.PostToUIThread(async () =>
{ {
await Task.Delay(500); await Task.Delay(500);
_renderElementService.AddLayerEffect(renderElement, SelectedLayerEffectDescriptor); renderElement.AddLayerEffect(SelectedLayerEffectDescriptor);
_profileEditorService.UpdateSelectedProfileElement(); _profileEditorService.UpdateSelectedProfileElement();
}); });
} }

View File

@ -16,11 +16,10 @@ using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using GongSolutions.Wpf.DragDrop; using GongSolutions.Wpf.DragDrop;
using Stylet; using Stylet;
using static Artemis.UI.Screens.ProfileEditor.LayerProperties.LayerPropertyGroupViewModel.ViewModelType;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties 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 ILayerPropertyVmFactory _layerPropertyVmFactory;
private readonly IDataBindingsVmFactory _dataBindingsVmFactory; private readonly IDataBindingsVmFactory _dataBindingsVmFactory;

View File

@ -1,58 +1,31 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Ninject.Factories; 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.Screens.ProfileEditor.LayerProperties.Tree;
using Artemis.UI.Shared.Services; using Stylet;
using Humanizer;
using Ninject;
using Ninject.Parameters;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
{ {
public class LayerPropertyGroupViewModel : LayerPropertyBaseViewModel public class LayerPropertyGroupViewModel : PropertyChangedBase, IDisposable
{ {
private readonly ILayerPropertyVmFactory _layerPropertyVmFactory; private readonly ILayerPropertyVmFactory _layerPropertyVmFactory;
private ViewModelType _groupType;
private TimelinePropertyGroupViewModel _timelinePropertyGroupViewModel;
private TreePropertyGroupViewModel _treePropertyGroupViewModel;
public LayerPropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, PropertyGroupDescriptionAttribute propertyGroupDescription, public LayerPropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, ILayerPropertyVmFactory layerPropertyVmFactory)
IProfileEditorService profileEditorService, ILayerPropertyVmFactory layerPropertyVmFactory)
{ {
_layerPropertyVmFactory = layerPropertyVmFactory; _layerPropertyVmFactory = layerPropertyVmFactory;
ProfileEditorService = profileEditorService;
LayerPropertyGroup = layerPropertyGroup; LayerPropertyGroup = layerPropertyGroup;
PropertyGroupDescription = propertyGroupDescription; LayerPropertyGroupTreeViewModel = layerPropertyVmFactory.LayerPropertyGroupTreeViewModel(this);
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;
PopulateChildren(); PopulateChildren();
DetermineType();
} }
public override bool IsVisible => !LayerPropertyGroup.IsHidden;
public IProfileEditorService ProfileEditorService { get; }
public LayerPropertyGroup LayerPropertyGroup { 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); get => LayerPropertyGroup.ProfileElement.IsPropertyGroupExpanded(LayerPropertyGroup);
set 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() private void PopulateChildren()
{ {
// Get all properties and property groups and create VMs for them // Get all properties and property groups and create VMs for them
// 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()) foreach (var propertyInfo in LayerPropertyGroup.GetType().GetProperties())
{ {
var propertyAttribute = (PropertyDescriptionAttribute) Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyDescriptionAttribute)); var propertyAttribute = (PropertyDescriptionAttribute) Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyDescriptionAttribute));
@ -144,50 +46,28 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
var value = propertyInfo.GetValue(LayerPropertyGroup); var value = propertyInfo.GetValue(LayerPropertyGroup);
// Create VMs for properties on the group // 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); var layerPropertyViewModel = _layerPropertyVmFactory.LayerPropertyViewModel(layerProperty);
if (viewModel != null) // After creation ensure a supported input VM was found, if not, discard the VM
Children.Add(viewModel); if (!layerPropertyViewModel.LayerPropertyTreeViewModel.HasPropertyInputViewModel)
layerPropertyViewModel.Dispose();
else
Children.Add(layerPropertyViewModel);
} }
// Create VMs for child groups on this group, resulting in a nested structure // Create VMs for child groups on this group, resulting in a nested structure
else if (groupAttribute != null && value is LayerPropertyGroup layerPropertyGroup) 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 foreach (var child in Children)
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[]
{ {
new ConstructorArgument("layerProperty", baseLayerProperty), if (child is IDisposable disposableChild)
new ConstructorArgument("propertyDescription", propertyDescription) disposableChild.Dispose();
}; }
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
} }
} }
} }

View File

@ -1,141 +1,37 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using Artemis.Core; 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.Screens.ProfileEditor.LayerProperties.Tree;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Humanizer;
using Ninject; using Ninject;
using Ninject.Parameters; using Ninject.Parameters;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
{ {
public class LayerPropertyViewModel<T> : LayerPropertyViewModel public class LayerPropertyViewModel : PropertyChangedBase, IDisposable
{ {
private TimelinePropertyViewModel<T> _timelinePropertyViewModel; public LayerPropertyViewModel(ILayerProperty layerProperty, IKernel kernel)
private TreePropertyViewModel<T> _treePropertyViewModel;
public LayerPropertyViewModel(IProfileEditorService profileEditorService, LayerProperty<T> layerProperty) : base(profileEditorService, layerProperty)
{ {
LayerProperty = layerProperty; LayerProperty = layerProperty;
TreePropertyViewModel = CreateTreePropertyViewModel(); var parameter = new ConstructorArgument("layerProperty", LayerProperty);
TimelinePropertyViewModel = new TimelinePropertyViewModel<T>(this, profileEditorService); var treeViewModelType = typeof(LayerPropertyTreeViewModel<>).MakeGenericType(layerProperty.GetType().GetGenericArguments());
var timelineViewModelType = typeof(LayerPropertyTimelineViewModel<>).MakeGenericType(layerProperty.GetType().GetGenericArguments());
TreePropertyBaseViewModel = TreePropertyViewModel; LayerPropertyTreeViewModel = (ILayerPropertyTreeViewModel) kernel.Get(treeViewModelType, parameter);
TimelinePropertyBaseViewModel = TimelinePropertyViewModel; LayerPropertyTimelineViewModel = (ILayerPropertyTimelineViewModel) kernel.Get(timelineViewModelType, parameter);
// 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;
} }
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; LayerPropertyTreeViewModel?.Dispose();
set => SetAndNotify(ref _treePropertyViewModel, value); LayerPropertyTimelineViewModel?.Dispose();
}
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);
} }
} }
} }

View File

@ -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
{
}
}

View File

@ -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;
}
}
}

View File

@ -9,7 +9,7 @@
xmlns:converters="clr-namespace:Artemis.UI.Converters" xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:dd="urn:gong-wpf-dragdrop" xmlns:dd="urn:gong-wpf-dragdrop"
mc:Ignorable="d" mc:Ignorable="d"
d:DataContext="{d:DesignInstance local:TreePropertyGroupViewModel}" d:DataContext="{d:DesignInstance local:LayerPropertyGroupTreeViewModel}"
d:DesignHeight="450" d:DesignWidth="800"> d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources> <UserControl.Resources>
<converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" /> <converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
@ -18,8 +18,8 @@
<!-- Type: None --> <!-- Type: None -->
<TextBlock <TextBlock
dd:DragDrop.DragSourceIgnore="True" dd:DragDrop.DragSourceIgnore="True"
Text="{Binding LayerPropertyGroupViewModel.PropertyGroupDescription.Name}" Text="{Binding LayerPropertyGroup.GroupDescription.Name}"
ToolTip="{Binding LayerPropertyGroupViewModel.PropertyGroupDescription.Description}" ToolTip="{Binding LayerPropertyGroup.GroupDescription.Description}"
VerticalAlignment="Center" VerticalAlignment="Center"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Margin="3 5 0 5"> Margin="3 5 0 5">
@ -27,7 +27,7 @@
<Style TargetType="{x:Type TextBlock}"> <Style TargetType="{x:Type TextBlock}">
<Setter Property="Visibility" Value="Collapsed" /> <Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers> <Style.Triggers>
<DataTrigger Binding="{Binding LayerPropertyGroupViewModel.GroupType}" Value="None"> <DataTrigger Binding="{Binding GroupType}" Value="None">
<Setter Property="Visibility" Value="Visible" /> <Setter Property="Visibility" Value="Visible" />
</DataTrigger> </DataTrigger>
</Style.Triggers> </Style.Triggers>
@ -41,14 +41,14 @@
<Style TargetType="{x:Type StackPanel}"> <Style TargetType="{x:Type StackPanel}">
<Setter Property="Visibility" Value="Collapsed" /> <Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers> <Style.Triggers>
<DataTrigger Binding="{Binding LayerPropertyGroupViewModel.GroupType}" Value="General"> <DataTrigger Binding="{Binding GroupType}" Value="General">
<Setter Property="Visibility" Value="Visible" /> <Setter Property="Visibility" Value="Visible" />
</DataTrigger> </DataTrigger>
</Style.Triggers> </Style.Triggers>
</Style> </Style>
</StackPanel.Style> </StackPanel.Style>
<materialDesign:PackIcon Kind="HammerWrench" Margin="0 -1 5 0" /> <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> </StackPanel>
<!-- Type: Transform --> <!-- Type: Transform -->
@ -57,14 +57,14 @@
<Style TargetType="{x:Type StackPanel}"> <Style TargetType="{x:Type StackPanel}">
<Setter Property="Visibility" Value="Collapsed" /> <Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers> <Style.Triggers>
<DataTrigger Binding="{Binding LayerPropertyGroupViewModel.GroupType}" Value="Transform"> <DataTrigger Binding="{Binding GroupType}" Value="Transform">
<Setter Property="Visibility" Value="Visible" /> <Setter Property="Visibility" Value="Visible" />
</DataTrigger> </DataTrigger>
</Style.Triggers> </Style.Triggers>
</Style> </Style>
</StackPanel.Style> </StackPanel.Style>
<materialDesign:PackIcon Kind="TransitConnectionVariant" Margin="0 -1 5 0" /> <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> </StackPanel>
<!-- Type: LayerBrushRoot --> <!-- Type: LayerBrushRoot -->
@ -79,23 +79,23 @@
<Style TargetType="{x:Type Grid}"> <Style TargetType="{x:Type Grid}">
<Setter Property="Visibility" Value="Collapsed" /> <Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers> <Style.Triggers>
<DataTrigger Binding="{Binding LayerPropertyGroupViewModel.GroupType}" Value="LayerBrushRoot"> <DataTrigger Binding="{Binding GroupType}" Value="LayerBrushRoot">
<Setter Property="Visibility" Value="Visible" /> <Setter Property="Visibility" Value="Visible" />
</DataTrigger> </DataTrigger>
</Style.Triggers> </Style.Triggers>
</Style> </Style>
</Grid.Style> </Grid.Style>
<materialDesign:PackIcon Grid.Column="0" <materialDesign:PackIcon Grid.Column="0"
Kind="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerBrush.Descriptor.Icon}" Kind="{Binding LayerPropertyGroup.LayerBrush.Descriptor.Icon}"
Margin="0 5 5 0" /> Margin="0 5 5 0" />
<TextBlock Grid.Column="1" <TextBlock Grid.Column="1"
ToolTip="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerBrush.Descriptor.Description}" ToolTip="{Binding LayerPropertyGroup.LayerBrush.Descriptor.Description}"
Margin="0 5 0 0"> Margin="0 5 0 0">
Brush -&#160; Brush -&#160;
</TextBlock> </TextBlock>
<TextBlock Grid.Column="2" <TextBlock Grid.Column="2"
Text="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerBrush.Descriptor.DisplayName}" Text="{Binding LayerPropertyGroup.LayerBrush.Descriptor.DisplayName}"
ToolTip="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerBrush.Descriptor.Description}" ToolTip="{Binding LayerPropertyGroup.LayerBrush.Descriptor.Description}"
Margin="0 5 0 0" /> Margin="0 5 0 0" />
<Button Grid.Column="3" <Button Grid.Column="3"
@ -106,7 +106,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
HorizontalAlignment="Right" HorizontalAlignment="Right"
Command="{s:Action OpenBrushSettings}" 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" /> <materialDesign:PackIcon Kind="Settings" Height="16" Width="16" />
</Button> </Button>
</Grid> </Grid>
@ -125,7 +125,7 @@
<Style TargetType="{x:Type Grid}"> <Style TargetType="{x:Type Grid}">
<Setter Property="Visibility" Value="Collapsed" /> <Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers> <Style.Triggers>
<DataTrigger Binding="{Binding LayerPropertyGroupViewModel.GroupType}" Value="LayerEffectRoot"> <DataTrigger Binding="{Binding GroupType}" Value="LayerEffectRoot">
<Setter Property="Visibility" Value="Visible" /> <Setter Property="Visibility" Value="Visible" />
</DataTrigger> </DataTrigger>
</Style.Triggers> </Style.Triggers>
@ -134,29 +134,29 @@
<materialDesign:PackIcon <materialDesign:PackIcon
Grid.Column="0" Grid.Column="0"
Cursor="SizeNS" Cursor="SizeNS"
Kind="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.Descriptor.Icon}" Kind="{Binding LayerPropertyGroup.LayerEffect.Descriptor.Icon}"
Margin="0 5 5 0" Margin="0 5 5 0"
Background="Transparent" /> 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 Effect
</TextBlock> </TextBlock>
<TextBlock Grid.Column="2" <TextBlock Grid.Column="2"
ToolTip="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.Descriptor.Description}" ToolTip="{Binding LayerPropertyGroup.LayerEffect.Descriptor.Description}"
Margin="3 5"> Margin="3 5">
- -
</TextBlock> </TextBlock>
<!-- Show either the descriptors display name or, if set, the effect name --> <!-- Show either the descriptors display name or, if set, the effect name -->
<TextBlock Grid.Column="3" <TextBlock Grid.Column="3"
Text="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.Descriptor.DisplayName}" Text="{Binding LayerPropertyGroup.LayerEffect.Descriptor.DisplayName}"
ToolTip="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.Descriptor.Description}" ToolTip="{Binding LayerPropertyGroup.LayerEffect.Descriptor.Description}"
Margin="0 5" 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" <TextBlock Grid.Column="4"
Text="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.Name}" Text="{Binding LayerPropertyGroup.LayerEffect.Name}"
ToolTip="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.Descriptor.Description}" ToolTip="{Binding LayerPropertyGroup.LayerEffect.Descriptor.Description}"
Margin="0 5" 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"> <StackPanel Grid.Column="5" Orientation="Horizontal">
<ToggleButton <ToggleButton
@ -164,7 +164,7 @@
ToolTip="Toggle enabled state" ToolTip="Toggle enabled state"
Width="18" Width="18"
Height="18" Height="18"
IsChecked="{Binding LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.Enabled}" IsChecked="{Binding LayerPropertyGroup.LayerEffect.Enabled}"
VerticalAlignment="Center" Padding="-25" VerticalAlignment="Center" Padding="-25"
Margin="5 0" Margin="5 0"
Command="{s:Action EnableToggled}"> Command="{s:Action EnableToggled}">
@ -184,7 +184,7 @@
Height="24" Height="24"
VerticalAlignment="Center" VerticalAlignment="Center"
Command="{s:Action OpenEffectSettings}" 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" /> <materialDesign:PackIcon Kind="Settings" Height="16" Width="16" />
</Button> </Button>
<Button Style="{StaticResource MaterialDesignIconButton}" <Button Style="{StaticResource MaterialDesignIconButton}"

View File

@ -1,13 +1,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using Artemis.Core;
using Artemis.Core.LayerBrushes; using Artemis.Core.LayerBrushes;
using Artemis.Core.LayerEffects; using Artemis.Core.LayerEffects;
using Artemis.Core.Services;
using Artemis.UI.Exceptions; using Artemis.UI.Exceptions;
using Artemis.UI.Screens.ProfileEditor.Dialogs; 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 Artemis.UI.Shared.Services;
using Ninject; using Ninject;
using Ninject.Parameters; using Ninject.Parameters;
@ -15,34 +14,36 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
{ {
public class TreePropertyGroupViewModel : PropertyChangedBase public class LayerPropertyGroupTreeViewModel : PropertyChangedBase
{ {
private readonly IDialogService _dialogService; private readonly IDialogService _dialogService;
private readonly IKernel _kernel; private readonly IKernel _kernel;
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private readonly IRenderElementService _renderElementService;
private readonly IWindowManager _windowManager; private readonly IWindowManager _windowManager;
public TreePropertyGroupViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel, public LayerPropertyGroupTreeViewModel(
LayerPropertyGroupViewModel layerPropertyGroupViewModel,
IProfileEditorService profileEditorService, IProfileEditorService profileEditorService,
IRenderElementService renderElementService,
IDialogService dialogService, IDialogService dialogService,
IWindowManager windowManager, IWindowManager windowManager,
IKernel kernel) IKernel kernel)
{ {
_profileEditorService = profileEditorService; _profileEditorService = profileEditorService;
_renderElementService = renderElementService;
_dialogService = dialogService; _dialogService = dialogService;
_windowManager = windowManager; _windowManager = windowManager;
_kernel = kernel; _kernel = kernel;
LayerPropertyGroupViewModel = (LayerPropertyGroupViewModel) layerPropertyBaseViewModel;
LayerPropertyGroupViewModel = layerPropertyGroupViewModel;
LayerPropertyGroup = LayerPropertyGroupViewModel.LayerPropertyGroup;
} }
public LayerPropertyGroupViewModel LayerPropertyGroupViewModel { get; } public LayerPropertyGroupViewModel LayerPropertyGroupViewModel { get; }
public LayerPropertyGroup LayerPropertyGroup { get; }
public LayerPropertyGroupType GroupType { get; set; }
public void OpenBrushSettings() public void OpenBrushSettings()
{ {
var layerBrush = LayerPropertyGroupViewModel.LayerPropertyGroup.LayerBrush; var layerBrush = LayerPropertyGroup.LayerBrush;
var configurationViewModel = layerBrush.ConfigurationDialog; var configurationViewModel = layerBrush.ConfigurationDialog;
if (configurationViewModel == null) if (configurationViewModel == null)
return; return;
@ -69,7 +70,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
public void OpenEffectSettings() public void OpenEffectSettings()
{ {
var layerEffect = LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect; var layerEffect = LayerPropertyGroup.LayerEffect;
var configurationViewModel = layerEffect.ConfigurationDialog; var configurationViewModel = layerEffect.ConfigurationDialog;
if (configurationViewModel == null) if (configurationViewModel == null)
return; return;
@ -100,20 +101,23 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
new Dictionary<string, object> new Dictionary<string, object>
{ {
{"subject", "effect"}, {"subject", "effect"},
{"currentName", LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.Name} {"currentName", LayerPropertyGroup.LayerEffect.Name}
} }
); );
if (result is string newName) if (result is string newName)
{ {
LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.Name = newName; LayerPropertyGroup.LayerEffect.Name = newName;
LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.HasBeenRenamed = true; LayerPropertyGroup.LayerEffect.HasBeenRenamed = true;
_profileEditorService.UpdateSelectedProfile(); _profileEditorService.UpdateSelectedProfile();
} }
} }
public void DeleteEffect() public void DeleteEffect()
{ {
_renderElementService.RemoveLayerEffect(LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect); if (LayerPropertyGroup.LayerEffect == null)
return;
LayerPropertyGroup.ProfileElement.RemoveLayerEffect(LayerPropertyGroup.LayerEffect);
_profileEditorService.UpdateSelectedProfile(); _profileEditorService.UpdateSelectedProfile();
} }
@ -121,5 +125,28 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
{ {
_profileEditorService.UpdateSelectedProfile(); _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
}
} }
} }

View File

@ -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="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
xmlns:tree="clr-namespace:Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" d:DesignHeight="450" d:DesignWidth="800">
d:DataContext="{d:DesignInstance {x:Type tree:TreePropertyViewModel}}">
<Grid Height="22" Margin="-20 0 0 0"> <Grid Height="22" Margin="-20 0 0 0">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
@ -22,7 +20,7 @@
Width="18" Width="18"
Height="18" Height="18"
IsChecked="{Binding KeyframesEnabled}" IsChecked="{Binding KeyframesEnabled}"
IsEnabled="{Binding LayerPropertyViewModel.LayerProperty.KeyframesSupported}" IsEnabled="{Binding LayerProperty.KeyframesSupported}"
VerticalAlignment="Center" Padding="-25"> VerticalAlignment="Center" Padding="-25">
<materialDesign:PackIcon Kind="Stopwatch" Height="13" Width="13" /> <materialDesign:PackIcon Kind="Stopwatch" Height="13" Width="13" />
</ToggleButton> </ToggleButton>
@ -32,8 +30,8 @@
Padding="0,0,5,0" Padding="0,0,5,0"
VerticalAlignment="Center" VerticalAlignment="Center"
TextTrimming="CharacterEllipsis" TextTrimming="CharacterEllipsis"
Text="{Binding LayerPropertyViewModel.LayerProperty.PropertyDescription.Name}" Text="{Binding LayerProperty.PropertyDescription.Name}"
ToolTip="{Binding LayerPropertyViewModel.LayerProperty.PropertyDescription.Description}" ToolTip="{Binding LayerProperty.PropertyDescription.Description}"
HorizontalAlignment="Left" /> HorizontalAlignment="Left" />
<ContentControl Grid.Column="2" Margin="20 0" s:View.Model="{Binding PropertyInputViewModel}"> <ContentControl Grid.Column="2" Margin="20 0" s:View.Model="{Binding PropertyInputViewModel}">
@ -51,7 +49,7 @@
Width="20" Width="20"
Height="20" Height="20"
VerticalAlignment="Center" VerticalAlignment="Center"
IsEnabled="{Binding LayerPropertyViewModel.LayerProperty.DataBindingsSupported}" IsEnabled="{Binding LayerProperty.DataBindingsSupported}"
IsChecked="{Binding DataBindingsOpen}"> IsChecked="{Binding DataBindingsOpen}">
<materialDesign:PackIcon Kind="VectorLink" Height="13" Width="13" /> <materialDesign:PackIcon Kind="VectorLink" Height="13" Width="13" />
</ToggleButton> </ToggleButton>

View File

@ -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; }
}
}

View File

@ -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();
}
}

View File

@ -7,7 +7,7 @@
xmlns:converters="clr-namespace:Artemis.UI.Converters" xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:dd="urn:gong-wpf-dragdrop" xmlns:dd="urn:gong-wpf-dragdrop"
xmlns:tree="clr-namespace:Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree" 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" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance {x:Type tree:TreeViewModel}}"> d:DataContext="{d:DesignInstance {x:Type tree:TreeViewModel}}">
@ -98,11 +98,11 @@
</Style> </Style>
</TreeView.ItemContainerStyle> </TreeView.ItemContainerStyle>
<TreeView.Resources> <TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type layerProperties1:LayerPropertyGroupViewModel}" ItemsSource="{Binding Children}"> <HierarchicalDataTemplate DataType="{x:Type local:LayerPropertyGroupViewModel}" ItemsSource="{Binding Items}">
<ContentControl s:View.Model="{Binding TreePropertyGroupViewModel}" /> <ContentControl s:View.Model="{Binding LayerPropertyGroupTreeViewModel}" />
</HierarchicalDataTemplate> </HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type layerProperties1:LayerPropertyViewModel}"> <DataTemplate DataType="{x:Type local:LayerPropertyViewModel}">
<ContentControl s:View.Model="{Binding TreePropertyBaseViewModel}" dd:DragDrop.DragSourceIgnore="True" /> <ContentControl s:View.Model="{Binding LayerPropertyTreeViewModel}" dd:DragDrop.DragSourceIgnore="True" />
</DataTemplate> </DataTemplate>
</TreeView.Resources> </TreeView.Resources>
</TreeView> </TreeView>

View File

@ -5,11 +5,13 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
{ {
public class TreeViewModel : PropertyChangedBase public class TreeViewModel : Screen
{ {
public TreeViewModel(LayerPropertiesViewModel layerPropertiesViewModel, BindableCollection<LayerPropertyGroupViewModel> layerPropertyGroups) public TreeViewModel(LayerPropertiesViewModel layerPropertiesViewModel, BindableCollection<LayerPropertyGroupViewModel> layerPropertyGroups)
{ {
LayerPropertiesViewModel = layerPropertiesViewModel; LayerPropertiesViewModel = layerPropertiesViewModel;
// Not using the Items collection because the list should persist even after this VM gets closed
LayerPropertyGroups = layerPropertyGroups; LayerPropertyGroups = layerPropertyGroups;
} }

View File

@ -17,7 +17,7 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor 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 IModuleService _moduleService;
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
@ -36,7 +36,7 @@ namespace Artemis.UI.Screens.ProfileEditor
private PluginSetting<GridLength> _sidePanelsWidth; private PluginSetting<GridLength> _sidePanelsWidth;
public ProfileEditorViewModel(ProfileModule module, public ProfileEditorViewModel(ProfileModule module,
ICollection<ProfileEditorPanelViewModel> viewModels, ICollection<IProfileEditorPanelViewModel> viewModels,
IProfileEditorService profileEditorService, IProfileEditorService profileEditorService,
IProfileService profileService, IProfileService profileService,
IDialogService dialogService, IDialogService dialogService,

View File

@ -27,7 +27,7 @@
</StackPanel> </StackPanel>
<TreeView Grid.Row="1" <TreeView Grid.Row="1"
ItemsSource="{Binding RootFolder.Children}" ItemsSource="{Binding ActiveItem.Items}"
HorizontalContentAlignment="Stretch" HorizontalContentAlignment="Stretch"
dd:DragDrop.IsDragSource="True" dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True" dd:DragDrop.IsDropTarget="True"
@ -36,11 +36,11 @@
<behaviors:TreeViewSelectionBehavior ExpandSelected="True" SelectedItem="{Binding SelectedTreeItem}" /> <behaviors:TreeViewSelectionBehavior ExpandSelected="True" SelectedItem="{Binding SelectedTreeItem}" />
</b:Interaction.Behaviors> </b:Interaction.Behaviors>
<TreeView.Resources> <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}" /> <ContentControl s:View.Model="{Binding}" />
</HierarchicalDataTemplate> </HierarchicalDataTemplate>
<!-- TODO: Ensure this item source is required --> <!-- 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}" /> <ContentControl s:View.Model="{Binding}" />
</HierarchicalDataTemplate> </HierarchicalDataTemplate>
</TreeView.Resources> </TreeView.Resources>

View File

@ -11,24 +11,17 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree 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 readonly IProfileEditorService _profileEditorService;
private FolderViewModel _rootFolder;
private TreeItemViewModel _selectedTreeItem; private TreeItemViewModel _selectedTreeItem;
private bool _updatingTree; private bool _updatingTree;
public ProfileTreeViewModel(IProfileEditorService profileEditorService, IFolderVmFactory folderVmFactory) public ProfileTreeViewModel(IProfileEditorService profileEditorService, IProfileTreeVmFactory profileTreeVmFactory)
{ {
_profileEditorService = profileEditorService; _profileEditorService = profileEditorService;
_folderVmFactory = folderVmFactory; _profileTreeVmFactory = profileTreeVmFactory;
}
public FolderViewModel RootFolder
{
get => _rootFolder;
set => SetAndNotify(ref _rootFolder, value);
} }
public TreeItemViewModel SelectedTreeItem public TreeItemViewModel SelectedTreeItem
@ -73,7 +66,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
switch (dragDropType) switch (dragDropType)
{ {
case DragDropType.Add: case DragDropType.Add:
source.Parent.RemoveExistingElement(source); ((TreeItemViewModel) source.Parent).RemoveExistingElement(source);
target.AddExistingElement(source); target.AddExistingElement(source);
break; break;
case DragDropType.InsertBefore: case DragDropType.InsertBefore:
@ -92,27 +85,25 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
// ReSharper disable once UnusedMember.Global - Called from view // ReSharper disable once UnusedMember.Global - Called from view
public void AddFolder() public void AddFolder()
{ {
RootFolder?.AddFolder(); ActiveItem?.AddFolder();
} }
// ReSharper disable once UnusedMember.Global - Called from view // ReSharper disable once UnusedMember.Global - Called from view
public void AddLayer() public void AddLayer()
{ {
RootFolder?.AddLayer(); ActiveItem?.AddLayer();
} }
protected override void OnInitialActivate() protected override void OnInitialActivate()
{ {
Subscribe(); Subscribe();
CreateRootFolderViewModel(); CreateRootFolderViewModel();
base.OnInitialActivate();
} }
protected override void OnClose() protected override void OnClose()
{ {
Unsubscribe(); Unsubscribe();
RootFolder?.Dispose();
RootFolder = null;
base.OnClose(); base.OnClose();
} }
@ -122,12 +113,11 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
var firstChild = _profileEditorService.SelectedProfile?.Children?.FirstOrDefault(); var firstChild = _profileEditorService.SelectedProfile?.Children?.FirstOrDefault();
if (!(firstChild is Folder folder)) if (!(firstChild is Folder folder))
{ {
RootFolder = null; ActivateItem(null);
return; return;
} }
RootFolder?.Dispose(); ActivateItem(_profileTreeVmFactory.FolderViewModel(folder));
RootFolder = _folderVmFactory.Create(folder);
_updatingTree = false; _updatingTree = false;
// Auto-select the first layer // Auto-select the first layer
@ -150,7 +140,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
{ {
if (parent == source) if (parent == source)
return DragDropType.None; return DragDropType.None;
parent = parent.Parent; parent = (TreeItemViewModel) parent.Parent;
} }
switch (dropInfo.InsertPosition) switch (dropInfo.InsertPosition)
@ -186,20 +176,20 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
if (e.RenderProfileElement == SelectedTreeItem?.ProfileElement) if (e.RenderProfileElement == SelectedTreeItem?.ProfileElement)
return; return;
if (RootFolder == null) if (ActiveItem == null)
{ {
CreateRootFolderViewModel(); CreateRootFolderViewModel();
return; return;
} }
_updatingTree = true; _updatingTree = true;
RootFolder.UpdateProfileElements(); ActiveItem.UpdateProfileElements();
_updatingTree = false; _updatingTree = false;
if (e.RenderProfileElement == null) if (e.RenderProfileElement == null)
SelectedTreeItem = null; SelectedTreeItem = null;
else else
{ {
var match = RootFolder.GetAllChildren().FirstOrDefault(vm => vm.ProfileElement == e.RenderProfileElement); var match = ActiveItem.GetAllChildren().FirstOrDefault(vm => vm.ProfileElement == e.RenderProfileElement);
if (match != null) if (match != null)
SelectedTreeItem = match; SelectedTreeItem = match;
} }

View File

@ -1,5 +1,4 @@
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories; using Artemis.UI.Ninject.Factories;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
@ -11,36 +10,11 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
public FolderViewModel(ProfileElement folder, public FolderViewModel(ProfileElement folder,
IProfileEditorService profileEditorService, IProfileEditorService profileEditorService,
IDialogService dialogService, IDialogService dialogService,
IRenderElementService renderElementService, IProfileTreeVmFactory profileTreeVmFactory) :
IFolderVmFactory folderVmFactory, base(folder, profileEditorService, dialogService, profileTreeVmFactory)
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)
{ {
} }
public override bool SupportsChildren => true; 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);
}
} }
} }

View File

@ -1,5 +1,4 @@
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories; using Artemis.UI.Ninject.Factories;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
@ -7,14 +6,11 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
{ {
public class LayerViewModel : TreeItemViewModel public class LayerViewModel : TreeItemViewModel
{ {
public LayerViewModel(TreeItemViewModel parent, public LayerViewModel(ProfileElement layer,
ProfileElement folder,
IProfileEditorService profileEditorService, IProfileEditorService profileEditorService,
IDialogService dialogService, IDialogService dialogService,
IRenderElementService renderElementService, IProfileTreeVmFactory profileTreeVmFactory) :
IFolderVmFactory folderVmFactory, base(layer, profileEditorService, dialogService, profileTreeVmFactory)
ILayerVmFactory layerVmFactory) :
base(parent, folder, profileEditorService, dialogService, renderElementService, folderVmFactory, layerVmFactory)
{ {
} }

View File

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Exceptions; using Artemis.UI.Exceptions;
using Artemis.UI.Ninject.Factories; using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.Dialogs; using Artemis.UI.Screens.ProfileEditor.Dialogs;
@ -12,66 +11,45 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem 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 IDialogService _dialogService;
private readonly IFolderVmFactory _folderVmFactory;
private readonly ILayerVmFactory _layerVmFactory;
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private readonly IRenderElementService _renderElementService; private readonly IProfileTreeVmFactory _profileTreeVmFactory;
private TreeItemViewModel _parent;
private ProfileElement _profileElement; private ProfileElement _profileElement;
protected TreeItemViewModel(TreeItemViewModel parent, protected TreeItemViewModel(ProfileElement profileElement,
ProfileElement profileElement,
IProfileEditorService profileEditorService, IProfileEditorService profileEditorService,
IDialogService dialogService, IDialogService dialogService,
IRenderElementService renderElementService, IProfileTreeVmFactory profileTreeVmFactory)
IFolderVmFactory folderVmFactory,
ILayerVmFactory layerVmFactory)
{ {
_profileEditorService = profileEditorService; _profileEditorService = profileEditorService;
_dialogService = dialogService; _dialogService = dialogService;
_renderElementService = renderElementService; _profileTreeVmFactory = profileTreeVmFactory;
_folderVmFactory = folderVmFactory;
_layerVmFactory = layerVmFactory;
Parent = parent;
ProfileElement = profileElement; ProfileElement = profileElement;
Children = new BindableCollection<TreeItemViewModel>();
Subscribe(); Subscribe();
UpdateProfileElements(); UpdateProfileElements();
} }
public TreeItemViewModel Parent
{
get => _parent;
set => SetAndNotify(ref _parent, value);
}
public ProfileElement ProfileElement public ProfileElement ProfileElement
{ {
get => _profileElement; get => _profileElement;
set => SetAndNotify(ref _profileElement, value); set => SetAndNotify(ref _profileElement, value);
} }
public BindableCollection<TreeItemViewModel> Children { get; }
public abstract bool SupportsChildren { get; } public abstract bool SupportsChildren { get; }
public void Dispose() public void Dispose()
{ {
Unsubscribe(); Unsubscribe();
Dispose(true);
GC.SuppressFinalize(this);
} }
public List<TreeItemViewModel> GetAllChildren() public List<TreeItemViewModel> GetAllChildren()
{ {
var children = new List<TreeItemViewModel>(); var children = new List<TreeItemViewModel>();
foreach (var childFolder in Children) foreach (var childFolder in Items)
{ {
// Add all children in this element // Add all children in this element
children.Add(childFolder); children.Add(childFolder);
@ -84,34 +62,38 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
public void SetElementInFront(TreeItemViewModel source) public void SetElementInFront(TreeItemViewModel source)
{ {
var sourceParent = (TreeItemViewModel) source.Parent;
var parent = (TreeItemViewModel) Parent;
if (source.Parent != Parent) if (source.Parent != Parent)
{ {
source.Parent.RemoveExistingElement(source); sourceParent.RemoveExistingElement(source);
Parent.AddExistingElement(source); parent.AddExistingElement(source);
} }
Parent.Unsubscribe(); parent.Unsubscribe();
Parent.ProfileElement.RemoveChild(source.ProfileElement); parent.ProfileElement.RemoveChild(source.ProfileElement);
Parent.ProfileElement.AddChild(source.ProfileElement, ProfileElement.Order); parent.ProfileElement.AddChild(source.ProfileElement, ProfileElement.Order);
Parent.Subscribe(); parent.Subscribe();
Parent.UpdateProfileElements(); parent.UpdateProfileElements();
} }
public void SetElementBehind(TreeItemViewModel source) public void SetElementBehind(TreeItemViewModel source)
{ {
var sourceParent = (TreeItemViewModel) source.Parent;
var parent = (TreeItemViewModel) Parent;
if (source.Parent != Parent) if (source.Parent != Parent)
{ {
source.Parent.RemoveExistingElement(source); sourceParent.RemoveExistingElement(source);
Parent.AddExistingElement(source); parent.AddExistingElement(source);
} }
Parent.Unsubscribe(); parent.Unsubscribe();
Parent.ProfileElement.RemoveChild(source.ProfileElement); parent.ProfileElement.RemoveChild(source.ProfileElement);
Parent.ProfileElement.AddChild(source.ProfileElement, ProfileElement.Order + 1); parent.ProfileElement.AddChild(source.ProfileElement, ProfileElement.Order + 1);
Parent.Subscribe(); parent.Subscribe();
Parent.UpdateProfileElements(); parent.UpdateProfileElements();
} }
public void RemoveExistingElement(TreeItemViewModel treeItem) public void RemoveExistingElement(TreeItemViewModel treeItem)
@ -138,7 +120,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
if (!SupportsChildren) if (!SupportsChildren)
throw new ArtemisUIException("Cannot add a folder to a profile element of type " + ProfileElement.GetType().Name); 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(); _profileEditorService.UpdateSelectedProfile();
} }
@ -147,7 +129,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
if (!SupportsChildren) if (!SupportsChildren)
throw new ArtemisUIException("Cannot add a layer to a profile element of type " + ProfileElement.GetType().Name); 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(); _profileEditorService.UpdateSelectedProfile();
} }
@ -180,7 +162,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
return; return;
// Farewell, cruel world // Farewell, cruel world
var parent = Parent; var parent = (TreeItemViewModel) Parent;
ProfileElement.Parent?.RemoveChild(ProfileElement); ProfileElement.Parent?.RemoveChild(ProfileElement);
parent.RemoveExistingElement(this); parent.RemoveExistingElement(this);
@ -190,17 +172,17 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
public void UpdateProfileElements() public void UpdateProfileElements()
{ {
// Remove VMs that are no longer a child // 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) foreach (var treeItemViewModel in toRemove)
Children.Remove(treeItemViewModel); DeactivateItem(treeItemViewModel);
// Order the children // 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++) for (var index = 0; index < vmsList.Count; index++)
{ {
var profileElementViewModel = vmsList[index]; var profileElementViewModel = vmsList[index];
if (Children.IndexOf(profileElementViewModel) != index) if (Items.IndexOf(profileElementViewModel) != index)
Children.Move(Children.IndexOf(profileElementViewModel), index); ((BindableCollection<TreeItemViewModel>) Items).Move(Items.IndexOf(profileElementViewModel), index);
} }
// Ensure every child element has an up-to-date VM // 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 (profileElement is Folder folder)
{ {
if (Children.FirstOrDefault(p => p is FolderViewModel vm && vm.ProfileElement == folder) == null) if (Items.FirstOrDefault(p => p is FolderViewModel vm && vm.ProfileElement == folder) == null)
newChildren.Add(_folderVmFactory.Create((FolderViewModel) this, folder)); newChildren.Add(_profileTreeVmFactory.FolderViewModel(folder));
} }
else if (profileElement is Layer layer) else if (profileElement is Layer layer)
{ {
if (Children.FirstOrDefault(p => p is LayerViewModel vm && vm.ProfileElement == layer) == null) if (Items.FirstOrDefault(p => p is LayerViewModel vm && vm.ProfileElement == layer) == null)
newChildren.Add(_layerVmFactory.Create((FolderViewModel) this, layer)); newChildren.Add(_profileTreeVmFactory.LayerViewModel(layer));
} }
} }
@ -229,7 +211,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
foreach (var treeItemViewModel in newChildren) foreach (var treeItemViewModel in newChildren)
{ {
treeItemViewModel.UpdateProfileElements(); treeItemViewModel.UpdateProfileElements();
Children.Add(treeItemViewModel); ActivateItem(treeItemViewModel);
} }
} }
@ -238,13 +220,6 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
_profileEditorService.UpdateSelectedProfile(); _profileEditorService.UpdateSelectedProfile();
} }
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
}
}
private void Subscribe() private void Subscribe()
{ {
ProfileElement.ChildAdded += ProfileElementOnChildAdded; ProfileElement.ChildAdded += ProfileElementOnChildAdded;

View File

@ -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>

View File

@ -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;
}
}
}

View File

@ -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>

View File

@ -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();
}
}
}
}

View File

@ -15,7 +15,7 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.Visualization 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 IProfileEditorService _profileEditorService;
private readonly IProfileLayerVmFactory _profileLayerVmFactory; private readonly IProfileLayerVmFactory _profileLayerVmFactory;

View File

@ -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="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

View File

@ -2,7 +2,7 @@
using Artemis.Core.LayerBrushes; using Artemis.Core.LayerBrushes;
using Stylet; using Stylet;
namespace Artemis.UI.Screens.ProfileEditor namespace Artemis.UI.Screens.ProfileEditor.Windows
{ {
public class LayerBrushSettingsWindowViewModel : Conductor<BrushConfigurationViewModel> public class LayerBrushSettingsWindowViewModel : Conductor<BrushConfigurationViewModel>
{ {

View File

@ -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="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

View File

@ -2,7 +2,7 @@
using Artemis.Core.LayerEffects; using Artemis.Core.LayerEffects;
using Stylet; using Stylet;
namespace Artemis.UI.Screens.ProfileEditor namespace Artemis.UI.Screens.ProfileEditor.Windows
{ {
public class LayerEffectSettingsWindowViewModel : Conductor<EffectConfigurationViewModel> public class LayerEffectSettingsWindowViewModel : Conductor<EffectConfigurationViewModel>
{ {