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

Core - Streamlined public parts of profile creation

UI - Started adjusting the VMs for the layer refactor
This commit is contained in:
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>
public abstract bool EvaluateObject(object target);
internal abstract void ApplyToEntity();
internal abstract void Save();
internal abstract DisplayConditionPartEntity GetEntity();
#region IDisposable

View File

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

View File

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

View File

@ -190,22 +190,31 @@ namespace Artemis.Core
return result;
}
internal override void ApplyToEntity()
internal override void Save()
{
Entity.PredicateType = (int) PredicateType;
Entity.ListDataModelGuid = ListDataModel?.PluginInfo?.Guid;
Entity.ListPropertyPath = ListPropertyPath;
if (ListDataModel != null)
{
Entity.ListDataModelGuid = ListDataModel.PluginInfo.Guid;
Entity.ListPropertyPath = ListPropertyPath;
}
Entity.LeftPropertyPath = LeftPropertyPath;
Entity.RightPropertyPath = RightPropertyPath;
Entity.RightStaticValue = JsonConvert.SerializeObject(RightStaticValue);
Entity.OperatorPluginGuid = Operator?.PluginInfo?.Guid;
Entity.OperatorType = Operator?.GetType().Name;
if (Operator != null)
{
Entity.OperatorPluginGuid = Operator.PluginInfo.Guid;
Entity.OperatorType = Operator.GetType().Name;
}
}
internal void Initialize()
private void Initialize()
{
ConditionOperatorStore.ConditionOperatorAdded += ConditionOperatorStoreOnConditionOperatorAdded;
ConditionOperatorStore.ConditionOperatorRemoved += ConditionOperatorStoreOnConditionOperatorRemoved;
// Left side
if (Entity.LeftPropertyPath != null && ListContainsInnerPath(Entity.LeftPropertyPath))
UpdateLeftSide(Entity.LeftPropertyPath);
@ -222,7 +231,7 @@ namespace Artemis.Core
if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightPropertyPath != null)
{
if (ListContainsInnerPath(Entity.RightPropertyPath))
UpdateLeftSide(Entity.LeftPropertyPath);
UpdateRightSideDynamic(Entity.RightPropertyPath);
}
// Right side static
else if (PredicateType == ProfileRightSideType.Static && Entity.RightStaticValue != null)
@ -384,5 +393,37 @@ namespace Artemis.Core
Expression.Property
);
}
#region Event handlers
private void ConditionOperatorStoreOnConditionOperatorAdded(object sender, ConditionOperatorStoreEvent e)
{
var conditionOperator = e.Registration.ConditionOperator;
if (Entity.OperatorPluginGuid == conditionOperator.PluginInfo.Guid && Entity.OperatorType == conditionOperator.GetType().Name)
UpdateOperator(conditionOperator);
}
private void ConditionOperatorStoreOnConditionOperatorRemoved(object sender, ConditionOperatorStoreEvent e)
{
if (e.Registration.ConditionOperator != Operator)
return;
Operator = null;
CompiledListPredicate = null;
}
#endregion
#region IDisposable
/// <inheritdoc />
protected override void Dispose(bool disposing)
{
ConditionOperatorStore.ConditionOperatorAdded -= ConditionOperatorStoreOnConditionOperatorAdded;
ConditionOperatorStore.ConditionOperatorRemoved -= ConditionOperatorStoreOnConditionOperatorRemoved;
base.Dispose(disposing);
}
#endregion
}
}

View File

@ -232,7 +232,7 @@ namespace Artemis.Core
return $"[Static] {LeftPropertyPath} {Operator.Description} {RightStaticValue}";
}
internal override void ApplyToEntity()
internal override void Save()
{
Entity.PredicateType = (int) PredicateType;
Entity.LeftDataModelGuid = LeftDataModel?.PluginInfo?.Guid;
@ -250,6 +250,8 @@ namespace Artemis.Core
{
DataModelStore.DataModelAdded += DataModelStoreOnDataModelAdded;
DataModelStore.DataModelRemoved += DataModelStoreOnDataModelRemoved;
ConditionOperatorStore.ConditionOperatorAdded += ConditionOperatorStoreOnConditionOperatorAdded;
ConditionOperatorStore.ConditionOperatorRemoved += ConditionOperatorStoreOnConditionOperatorRemoved;
// Left side
if (Entity.LeftDataModelGuid != null)
@ -415,19 +417,6 @@ namespace Artemis.Core
);
}
private Expression CreateListAccessor(DataModel dataModel, string path, ParameterExpression listParameter)
{
var listType = dataModel.GetListTypeInPath(path);
if (listType == null)
throw new ArtemisCoreException($"Cannot create a list accessor at path {path} because the path does not contain a list");
path = dataModel.GetListInnerPath(path);
return path.Split('.').Aggregate<string, Expression>(
Expression.Convert(listParameter, listType), // Cast to the appropriate type
Expression.Property
);
}
#region Event handlers
private void DataModelStoreOnDataModelAdded(object sender, DataModelStoreEvent e)
@ -454,6 +443,23 @@ namespace Artemis.Core
}
}
private void ConditionOperatorStoreOnConditionOperatorAdded(object sender, ConditionOperatorStoreEvent e)
{
var conditionOperator = e.Registration.ConditionOperator;
if (Entity.OperatorPluginGuid == conditionOperator.PluginInfo.Guid && Entity.OperatorType == conditionOperator.GetType().Name)
UpdateOperator(conditionOperator);
}
private void ConditionOperatorStoreOnConditionOperatorRemoved(object sender, ConditionOperatorStoreEvent e)
{
if (e.Registration.ConditionOperator != Operator)
return;
Operator = null;
CompiledStaticPredicate = null;
CompiledDynamicPredicate = null;
}
#endregion
/// <inheritdoc />
@ -461,6 +467,8 @@ namespace Artemis.Core
{
DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded;
DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved;
ConditionOperatorStore.ConditionOperatorAdded -= ConditionOperatorStoreOnConditionOperatorAdded;
ConditionOperatorStore.ConditionOperatorRemoved -= ConditionOperatorStoreOnConditionOperatorRemoved;
base.Dispose(disposing);
}

View File

@ -8,24 +8,35 @@ using SkiaSharp;
namespace Artemis.Core
{
/// <summary>
/// Represents a folder in a <see cref="Profile" />
/// </summary>
public sealed class Folder : RenderProfileElement
{
private SKBitmap _folderBitmap;
public Folder(Profile profile, ProfileElement parent, string name)
/// <summary>
/// Creates a new instance of the <see cref="Folder" /> class and adds itself to the child collection of the provided
/// <paramref name="parent" />
/// </summary>
/// <param name="parent">The parent of the folder</param>
/// <param name="name">The name of the folder</param>
public Folder(ProfileElement parent, string name)
{
FolderEntity = new FolderEntity();
EntityId = Guid.NewGuid();
Profile = profile;
Parent = parent;
Parent = parent ?? throw new ArgumentNullException(nameof(parent));
Profile = Parent.Profile;
Name = name;
Enabled = true;
DisplayContinuously = true;
_layerEffects = new List<BaseLayerEffect>();
_expandedPropertyGroups = new List<string>();
ApplyRenderElementDefaults();
Parent.AddChild(this);
}
internal Folder(Profile profile, ProfileElement parent, FolderEntity folderEntity)
@ -182,22 +193,7 @@ namespace Artemis.Core
canvas.Restore();
}
/// <summary>
/// Adds a new folder to the bottom of this folder
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public Folder AddFolder(string name)
{
if (_disposed)
throw new ObjectDisposedException("Folder");
var folder = new Folder(Profile, this, name) {Order = Children.LastOrDefault()?.Order ?? 1};
AddChild(folder);
return folder;
}
/// <inheritdoc />
public override void AddChild(ProfileElement child, int? order = null)
{
@ -296,7 +292,7 @@ namespace Artemis.Core
// Conditions
RenderElementEntity.RootDisplayCondition = DisplayConditionGroup?.Entity;
DisplayConditionGroup?.ApplyToEntity();
DisplayConditionGroup?.Save();
SaveRenderElement();
}

View File

@ -11,8 +11,7 @@ using SkiaSharp;
namespace Artemis.Core
{
/// <summary>
/// Represents a layer on a profile. To create new layers use the <see cref="RenderElementService" /> by injecting
/// <see cref="IRenderElementService" /> into your code
/// Represents a layer in a <see cref="Profile" />
/// </summary>
public sealed class Layer : RenderProfileElement
{
@ -23,13 +22,19 @@ namespace Artemis.Core
private List<ArtemisLed> _leds;
private LayerTransformProperties _transform;
internal Layer(Profile profile, ProfileElement parent, string name)
/// <summary>
/// Creates a new instance of the <see cref="Layer" /> class and adds itself to the child collection of the provided
/// <paramref name="parent" />
/// </summary>
/// <param name="parent">The parent of the layer</param>
/// <param name="name">The name of the layer</param>
public Layer(ProfileElement parent, string name)
{
LayerEntity = new LayerEntity();
EntityId = Guid.NewGuid();
Profile = profile;
Parent = parent;
Parent = parent ?? throw new ArgumentNullException(nameof(parent));
Profile = Parent.Profile;
Name = name;
Enabled = true;
DisplayContinuously = true;
@ -40,10 +45,10 @@ namespace Artemis.Core
_leds = new List<ArtemisLed>();
_expandedPropertyGroups = new List<string>();
InitializeDefaultGroups();
parent.AddChild(this);
Initialize();
ApplyRenderElementDefaults();
Parent.AddChild(this);
}
internal Layer(Profile profile, ProfileElement parent, LayerEntity layerEntity)
@ -58,8 +63,7 @@ namespace Artemis.Core
_leds = new List<ArtemisLed>();
_expandedPropertyGroups = new List<string>();
InitializeDefaultGroups();
Initialize();
Load();
}
@ -114,6 +118,8 @@ namespace Artemis.Core
return $"[Layer] {nameof(Name)}: {Name}, {nameof(Order)}: {Order}";
}
#region IDisposable
/// <inheritdoc />
protected override void Dispose(bool disposing)
{
@ -133,8 +139,13 @@ namespace Artemis.Core
_transform?.Dispose();
}
private void InitializeDefaultGroups()
#endregion
private void Initialize()
{
LayerBrushStore.LayerBrushAdded += LayerBrushStoreOnLayerBrushAdded;
LayerBrushStore.LayerBrushRemoved += LayerBrushStoreOnLayerBrushRemoved;
// Layers have two hardcoded property groups, instantiate them
General.Initialize(this, "General.", Constants.CorePluginInfo);
Transform.Initialize(this, "Transform.", Constants.CorePluginInfo);
@ -191,7 +202,7 @@ namespace Artemis.Core
// Conditions
RenderElementEntity.RootDisplayCondition = DisplayConditionGroup?.Entity;
DisplayConditionGroup?.ApplyToEntity();
DisplayConditionGroup?.Save();
SaveRenderElement();
}
@ -711,6 +722,27 @@ namespace Artemis.Core
#endregion
#region Event handlers
private void LayerBrushStoreOnLayerBrushRemoved(object sender, LayerBrushStoreEvent e)
{
if (LayerBrush.Descriptor == e.Registration.LayerBrushDescriptor)
DeactivateLayerBrush();
}
private void LayerBrushStoreOnLayerBrushAdded(object sender, LayerBrushStoreEvent e)
{
if (LayerBrush != null)
return;
var current = General.BrushReference.CurrentValue;
if (e.Registration.Plugin.PluginInfo.Guid == current.BrushPluginGuid &&
e.Registration.LayerBrushDescriptor.LayerBrushType.Name == current.BrushType)
ActivateLayerBrush();
}
#endregion
#region Events
public event EventHandler RenderPropertiesUpdated;

View File

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

View File

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

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Artemis.Core.LayerEffects;
using Artemis.Core.LayerEffects.Placeholder;
using Artemis.Core.Properties;
using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile.Abstract;
@ -49,7 +50,7 @@ namespace Artemis.Core
{
Id = layerEffect.EntityId,
PluginGuid = layerEffect.PluginInfo.Guid,
EffectType = layerEffect.GetType().Name,
EffectType = layerEffect.GetEffectTypeName(),
Name = layerEffect.Name,
Enabled = layerEffect.Enabled,
HasBeenRenamed = layerEffect.HasBeenRenamed,
@ -268,11 +269,29 @@ namespace Artemis.Core
{
foreach (var layerEffectEntity in RenderElementEntity.LayerEffects)
{
if (_layerEffects.Any(e => e.EntityId == layerEffectEntity.Id))
// If there is a non-placeholder existing effect, skip this entity
var existing = _layerEffects.FirstOrDefault(e => e.EntityId == layerEffectEntity.Id);
if (existing != null && !existing.Descriptor.IsPlaceHolder)
continue;
var descriptor = LayerEffectStore.Get(layerEffectEntity.PluginGuid, layerEffectEntity.EffectType)?.LayerEffectDescriptor;
descriptor?.CreateInstance(this, layerEffectEntity);
if (descriptor != null)
{
// If a descriptor is found but there is an existing placeholder, remove the placeholder
if (existing != null)
{
_layerEffects.Remove(existing);
existing.Dispose();
}
// Create an instance with the descriptor
descriptor.CreateInstance(this, layerEffectEntity);
}
else if (existing == null)
{
// If no descriptor was found and there was no existing placeholder, create a placeholder
descriptor = PlaceholderLayerEffectDescriptor.Create();
descriptor.CreateInstance(this, layerEffectEntity);
}
}
}
@ -297,9 +316,10 @@ namespace Artemis.Core
throw new NotImplementedException();
}
private void LayerEffectStoreOnLayerEffectAdded(object? sender, LayerEffectStoreEvent e)
private void LayerEffectStoreOnLayerEffectAdded(object sender, LayerEffectStoreEvent e)
{
ActivateEffects();
if (RenderElementEntity.LayerEffects.Any(ef => ef.PluginGuid == e.Registration.Plugin.PluginInfo.Guid))
ActivateEffects();
}
#endregion

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
// but LayerEffect<T> outside the core
internal abstract void Initialize();
internal virtual string GetEffectTypeName() => GetType().Name;
}
}

View File

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

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)
{
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; }
RenderProfileElement SelectedProfileElement { get; }
BaseLayerProperty SelectedDataBinding { get; }
ILayerProperty SelectedDataBinding { get; }
TimeSpan CurrentTime { get; set; }
int PixelsPerSecond { get; set; }
IReadOnlyList<PropertyInputRegistration> RegisteredPropertyEditors { get; }
@ -19,7 +19,7 @@ namespace Artemis.UI.Shared.Services
void UpdateSelectedProfile();
void ChangeSelectedProfileElement(RenderProfileElement profileElement);
void UpdateSelectedProfileElement();
void ChangeSelectedDataBinding(BaseLayerProperty layerProperty);
void ChangeSelectedDataBinding(ILayerProperty layerProperty);
void UpdateProfilePreview();
bool UndoUpdateProfile();
bool RedoUpdateProfile();
@ -88,5 +88,10 @@ namespace Artemis.UI.Shared.Services
/// <param name="excludedKeyframe">A keyframe to exclude during keyframe snapping</param>
/// <returns></returns>
TimeSpan SnapToTimeline(TimeSpan time, TimeSpan tolerance, bool snapToSegments, bool snapToCurrentTime, bool snapToKeyframes, BaseLayerPropertyKeyframe excludedKeyframe = null);
/// <summary>
/// If a matching registration is found, creates a new <see cref="PropertyInputViewModel{T}"/> supporting <typeparamref name="T"/>
/// </summary>
PropertyInputViewModel<T> CreatePropertyInputViewModel<T>(LayerProperty<T> layerProperty);
}
}

View File

@ -5,6 +5,7 @@ using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.Core.Services;
using Ninject;
using Ninject.Parameters;
using Serilog;
using Stylet;
@ -269,6 +270,16 @@ namespace Artemis.UI.Shared.Services
return time;
}
public PropertyInputViewModel<T> CreatePropertyInputViewModel<T>(LayerProperty<T> layerProperty)
{
var registration = RegisteredPropertyEditors.FirstOrDefault(r => r.SupportedType == typeof(T));
if (registration == null)
return null;
var parameter = new ConstructorArgument("layerProperty", layerProperty);
return (PropertyInputViewModel<T>) Kernel.Get(registration.ViewModelType, parameter);
}
public ProfileModule GetCurrentModule()
{
return SelectedProfile?.Module;

View File

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

View File

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

View File

@ -1,44 +1,23 @@
using System;
using Artemis.Core;
using Artemis.Core;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract
{
public abstract class DisplayConditionViewModel : PropertyChangedBase, IDisposable
public abstract class DisplayConditionViewModel : Conductor<DisplayConditionViewModel>.Collection.AllActive
{
protected DisplayConditionViewModel(DisplayConditionPart model, DisplayConditionViewModel parent)
protected DisplayConditionViewModel(DisplayConditionPart model)
{
Model = model;
Parent = parent;
Children = new BindableCollection<DisplayConditionViewModel>();
}
public DisplayConditionPart Model { get; }
public DisplayConditionViewModel Parent { get; set; }
public BindableCollection<DisplayConditionViewModel> Children { get; }
public void Dispose()
{
foreach (var child in Children)
child.Dispose();
Dispose(true);
GC.SuppressFinalize(this);
}
public abstract void Update();
public virtual void Delete()
{
Model.Parent.RemoveChild(Model);
Parent.Update();
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
}
((DisplayConditionViewModel) Parent).Update();
}
}
}

View File

@ -124,7 +124,7 @@
<materialDesign:PackIcon Kind="Add" Width="18" Height="18" />
</Button>
<ItemsControl Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" ItemsSource="{Binding Children}">
<ItemsControl Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<materialDesign:TransitioningContent>

View File

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

View File

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

View File

@ -105,7 +105,7 @@
</Button.ContextMenu>
</Button>
<ItemsControl Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" ItemsSource="{Binding Children}" Margin="0 4 0 0">
<ItemsControl Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" ItemsSource="{Binding Items}" Margin="0 4 0 0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<materialDesign:TransitioningContent>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -16,11 +16,10 @@ using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using GongSolutions.Wpf.DragDrop;
using Stylet;
using static Artemis.UI.Screens.ProfileEditor.LayerProperties.LayerPropertyGroupViewModel.ViewModelType;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
{
public class LayerPropertiesViewModel : ProfileEditorPanelViewModel, IDropTarget
public class LayerPropertiesViewModel : Conductor<IScreen>.Collection.AllActive, IProfileEditorPanelViewModel, IDropTarget
{
private readonly ILayerPropertyVmFactory _layerPropertyVmFactory;
private readonly IDataBindingsVmFactory _dataBindingsVmFactory;

View File

@ -1,58 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Artemis.Core;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Abstract;
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline;
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree;
using Artemis.UI.Shared.Services;
using Humanizer;
using Ninject;
using Ninject.Parameters;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
{
public class LayerPropertyGroupViewModel : LayerPropertyBaseViewModel
public class LayerPropertyGroupViewModel : PropertyChangedBase, IDisposable
{
private readonly ILayerPropertyVmFactory _layerPropertyVmFactory;
private ViewModelType _groupType;
private TimelinePropertyGroupViewModel _timelinePropertyGroupViewModel;
private TreePropertyGroupViewModel _treePropertyGroupViewModel;
public LayerPropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, PropertyGroupDescriptionAttribute propertyGroupDescription,
IProfileEditorService profileEditorService, ILayerPropertyVmFactory layerPropertyVmFactory)
public LayerPropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, ILayerPropertyVmFactory layerPropertyVmFactory)
{
_layerPropertyVmFactory = layerPropertyVmFactory;
ProfileEditorService = profileEditorService;
LayerPropertyGroup = layerPropertyGroup;
PropertyGroupDescription = propertyGroupDescription;
TreePropertyGroupViewModel = _layerPropertyVmFactory.TreePropertyGroupViewModel(this);
TimelinePropertyGroupViewModel = _layerPropertyVmFactory.TimelinePropertyGroupViewModel(this);
// Generate a fallback name if the description does not contain one
if (PropertyGroupDescription.Name == null)
{
var propertyInfo = LayerPropertyGroup.Parent?.GetType().GetProperties().FirstOrDefault(p => ReferenceEquals(p.GetValue(LayerPropertyGroup.Parent), LayerPropertyGroup));
if (propertyInfo != null)
PropertyGroupDescription.Name = propertyInfo.Name.Humanize();
else
PropertyGroupDescription.Name = "Unknown group";
}
LayerPropertyGroup.VisibilityChanged += LayerPropertyGroupOnVisibilityChanged;
LayerPropertyGroupTreeViewModel = layerPropertyVmFactory.LayerPropertyGroupTreeViewModel(this);
PopulateChildren();
DetermineType();
}
public override bool IsVisible => !LayerPropertyGroup.IsHidden;
public IProfileEditorService ProfileEditorService { get; }
public LayerPropertyGroup LayerPropertyGroup { get; }
public PropertyGroupDescriptionAttribute PropertyGroupDescription { get; }
public LayerPropertyGroupTreeViewModel LayerPropertyGroupTreeViewModel { get; }
public BindableCollection<PropertyChangedBase> Children { get; set; }
public override bool IsExpanded
public bool IsVisible => !LayerPropertyGroup.IsHidden;
public bool IsExpanded
{
get => LayerPropertyGroup.ProfileElement.IsPropertyGroupExpanded(LayerPropertyGroup);
set
@ -62,81 +35,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
}
}
public ViewModelType GroupType
{
get => _groupType;
set => SetAndNotify(ref _groupType, value);
}
public TreePropertyGroupViewModel TreePropertyGroupViewModel
{
get => _treePropertyGroupViewModel;
set => SetAndNotify(ref _treePropertyGroupViewModel, value);
}
public TimelinePropertyGroupViewModel TimelinePropertyGroupViewModel
{
get => _timelinePropertyGroupViewModel;
set => SetAndNotify(ref _timelinePropertyGroupViewModel, value);
}
public override List<BaseLayerPropertyKeyframe> GetKeyframes(bool expandedOnly)
{
var result = new List<BaseLayerPropertyKeyframe>();
if (expandedOnly && !IsExpanded || LayerPropertyGroup.IsHidden)
return result;
foreach (var layerPropertyBaseViewModel in Children)
result.AddRange(layerPropertyBaseViewModel.GetKeyframes(expandedOnly));
return result;
}
public override void Dispose()
{
foreach (var layerPropertyBaseViewModel in Children)
layerPropertyBaseViewModel.Dispose();
LayerPropertyGroup.VisibilityChanged -= LayerPropertyGroupOnVisibilityChanged;
TimelinePropertyGroupViewModel.Dispose();
}
public List<LayerPropertyBaseViewModel> GetAllChildren()
{
var result = new List<LayerPropertyBaseViewModel>();
foreach (var layerPropertyBaseViewModel in Children)
{
result.Add(layerPropertyBaseViewModel);
if (layerPropertyBaseViewModel is LayerPropertyGroupViewModel layerPropertyGroupViewModel)
result.AddRange(layerPropertyGroupViewModel.GetAllChildren());
}
return result;
}
public void UpdateOrder(int order)
{
LayerPropertyGroup.LayerEffect.Order = order;
NotifyOfPropertyChange(nameof(IsExpanded));
}
private void DetermineType()
{
if (LayerPropertyGroup is LayerGeneralProperties)
GroupType = ViewModelType.General;
else if (LayerPropertyGroup is LayerTransformProperties)
GroupType = ViewModelType.Transform;
else if (LayerPropertyGroup.Parent == null && LayerPropertyGroup.LayerBrush != null)
GroupType = ViewModelType.LayerBrushRoot;
else if (LayerPropertyGroup.Parent == null && LayerPropertyGroup.LayerEffect != null)
GroupType = ViewModelType.LayerEffectRoot;
else
GroupType = ViewModelType.None;
}
private void PopulateChildren()
{
// Get all properties and property groups and create VMs for them
// The group has methods for getting this without reflection but then we lose the order of the properties as they are defined on the group
foreach (var propertyInfo in LayerPropertyGroup.GetType().GetProperties())
{
var propertyAttribute = (PropertyDescriptionAttribute) Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyDescriptionAttribute));
@ -144,50 +46,28 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
var value = propertyInfo.GetValue(LayerPropertyGroup);
// Create VMs for properties on the group
if (propertyAttribute != null && value is BaseLayerProperty baseLayerProperty)
if (propertyAttribute != null && value is ILayerProperty layerProperty)
{
var viewModel = CreateLayerPropertyViewModel(baseLayerProperty, propertyAttribute);
if (viewModel != null)
Children.Add(viewModel);
var layerPropertyViewModel = _layerPropertyVmFactory.LayerPropertyViewModel(layerProperty);
// After creation ensure a supported input VM was found, if not, discard the VM
if (!layerPropertyViewModel.LayerPropertyTreeViewModel.HasPropertyInputViewModel)
layerPropertyViewModel.Dispose();
else
Children.Add(layerPropertyViewModel);
}
// Create VMs for child groups on this group, resulting in a nested structure
else if (groupAttribute != null && value is LayerPropertyGroup layerPropertyGroup)
Children.Add(_layerPropertyVmFactory.LayerPropertyGroupViewModel(layerPropertyGroup, groupAttribute));
Children.Add(_layerPropertyVmFactory.LayerPropertyGroupViewModel(layerPropertyGroup));
}
}
private LayerPropertyBaseViewModel CreateLayerPropertyViewModel(BaseLayerProperty baseLayerProperty, PropertyDescriptionAttribute propertyDescription)
public void Dispose()
{
// Go through the pain of instantiating a generic type VM now via reflection to make things a lot simpler down the line
var genericType = baseLayerProperty.GetType().Name == typeof(LayerProperty<>).Name
? baseLayerProperty.GetType().GetGenericArguments()[0]
: baseLayerProperty.GetType().BaseType.GetGenericArguments()[0];
// Only create entries for types supported by a tree input VM
if (!genericType.IsEnum && ProfileEditorService.RegisteredPropertyEditors.All(r => r.SupportedType != genericType))
return null;
var genericViewModel = typeof(LayerPropertyViewModel<>).MakeGenericType(genericType);
var parameters = new IParameter[]
foreach (var child in Children)
{
new ConstructorArgument("layerProperty", baseLayerProperty),
new ConstructorArgument("propertyDescription", propertyDescription)
};
return (LayerPropertyBaseViewModel) ProfileEditorService.Kernel.Get(genericViewModel, parameters);
}
private void LayerPropertyGroupOnVisibilityChanged(object sender, EventArgs e)
{
NotifyOfPropertyChange(nameof(IsVisible));
}
public enum ViewModelType
{
General,
Transform,
LayerBrushRoot,
LayerEffectRoot,
None
if (child is IDisposable disposableChild)
disposableChild.Dispose();
}
}
}
}

View File

@ -1,141 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Artemis.Core;
using Artemis.UI.Exceptions;
using Artemis.UI.PropertyInput;
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Abstract;
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline;
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Humanizer;
using Ninject;
using Ninject.Parameters;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
{
public class LayerPropertyViewModel<T> : LayerPropertyViewModel
public class LayerPropertyViewModel : PropertyChangedBase, IDisposable
{
private TimelinePropertyViewModel<T> _timelinePropertyViewModel;
private TreePropertyViewModel<T> _treePropertyViewModel;
public LayerPropertyViewModel(IProfileEditorService profileEditorService, LayerProperty<T> layerProperty) : base(profileEditorService, layerProperty)
public LayerPropertyViewModel(ILayerProperty layerProperty, IKernel kernel)
{
LayerProperty = layerProperty;
TreePropertyViewModel = CreateTreePropertyViewModel();
TimelinePropertyViewModel = new TimelinePropertyViewModel<T>(this, profileEditorService);
var parameter = new ConstructorArgument("layerProperty", LayerProperty);
var treeViewModelType = typeof(LayerPropertyTreeViewModel<>).MakeGenericType(layerProperty.GetType().GetGenericArguments());
var timelineViewModelType = typeof(LayerPropertyTimelineViewModel<>).MakeGenericType(layerProperty.GetType().GetGenericArguments());
TreePropertyBaseViewModel = TreePropertyViewModel;
TimelinePropertyBaseViewModel = TimelinePropertyViewModel;
// Generate a fallback name if the description does not contain one
if (LayerProperty.PropertyDescription.Name == null)
{
var propertyInfo = LayerProperty.Parent?.GetType().GetProperties().FirstOrDefault(p => ReferenceEquals(p.GetValue(LayerProperty.Parent), LayerProperty));
if (propertyInfo != null)
LayerProperty.PropertyDescription.Name = propertyInfo.Name.Humanize();
else
LayerProperty.PropertyDescription.Name = $"Unknown {typeof(T).Name} property";
}
LayerProperty.VisibilityChanged += LayerPropertyOnVisibilityChanged;
LayerPropertyTreeViewModel = (ILayerPropertyTreeViewModel) kernel.Get(treeViewModelType, parameter);
LayerPropertyTimelineViewModel = (ILayerPropertyTimelineViewModel) kernel.Get(timelineViewModelType, parameter);
}
public override bool IsVisible => !LayerProperty.IsHidden;
public ILayerProperty LayerProperty { get; }
public ILayerPropertyTreeViewModel LayerPropertyTreeViewModel { get; }
public ILayerPropertyTimelineViewModel LayerPropertyTimelineViewModel { get; }
public LayerProperty<T> LayerProperty { get; }
public bool IsVisible { get; set; }
public bool IsExpanded { get; set; }
public TreePropertyViewModel<T> TreePropertyViewModel
public void Dispose()
{
get => _treePropertyViewModel;
set => SetAndNotify(ref _treePropertyViewModel, value);
}
public TimelinePropertyViewModel<T> TimelinePropertyViewModel
{
get => _timelinePropertyViewModel;
set => SetAndNotify(ref _timelinePropertyViewModel, value);
}
public override List<BaseLayerPropertyKeyframe> GetKeyframes(bool expandedOnly)
{
if (LayerProperty.KeyframesEnabled && !LayerProperty.IsHidden)
return LayerProperty.BaseKeyframes.ToList();
return new List<BaseLayerPropertyKeyframe>();
}
public override void Dispose()
{
TreePropertyViewModel.Dispose();
TimelinePropertyViewModel.Dispose();
LayerProperty.VisibilityChanged -= LayerPropertyOnVisibilityChanged;
}
public void SetCurrentValue(T value, bool saveChanges)
{
LayerProperty.SetCurrentValue(value, ProfileEditorService.CurrentTime);
if (saveChanges)
ProfileEditorService.UpdateSelectedProfileElement();
else
ProfileEditorService.UpdateProfilePreview();
}
private TreePropertyViewModel<T> CreateTreePropertyViewModel()
{
// Make sure there is a supported property editor VM, unless the type is an enum, then we'll use the EnumPropertyInputViewModel
Type vmType = null;
if (typeof(T).IsEnum)
vmType = typeof(EnumPropertyInputViewModel<>).MakeGenericType(typeof(T));
else
{
var registration = ProfileEditorService.RegisteredPropertyEditors.FirstOrDefault(r => r.SupportedType == typeof(T));
if (registration != null)
vmType = registration.ViewModelType;
}
if (vmType == null)
throw new ArtemisUIException($"Cannot create a tree property view model for type {typeof(T)}, found no matching property editor");
var parameters = new IParameter[]
{
new ConstructorArgument("layerProperty", LayerProperty)
};
return new TreePropertyViewModel<T>(this, (PropertyInputViewModel<T>) ProfileEditorService.Kernel.Get(vmType, parameters), ProfileEditorService);
}
private void LayerPropertyOnVisibilityChanged(object sender, EventArgs e)
{
NotifyOfPropertyChange(nameof(IsVisible));
}
}
public abstract class LayerPropertyViewModel : LayerPropertyBaseViewModel
{
private TimelinePropertyViewModel _timelinePropertyBaseViewModel;
private TreePropertyViewModel _treePropertyBaseViewModel;
protected LayerPropertyViewModel(IProfileEditorService profileEditorService, BaseLayerProperty baseLayerProperty)
{
ProfileEditorService = profileEditorService;
BaseLayerProperty = baseLayerProperty;
}
public IProfileEditorService ProfileEditorService { get; }
public BaseLayerProperty BaseLayerProperty { get; }
public TreePropertyViewModel TreePropertyBaseViewModel
{
get => _treePropertyBaseViewModel;
set => SetAndNotify(ref _treePropertyBaseViewModel, value);
}
public TimelinePropertyViewModel TimelinePropertyBaseViewModel
{
get => _timelinePropertyBaseViewModel;
set => SetAndNotify(ref _timelinePropertyBaseViewModel, value);
LayerPropertyTreeViewModel?.Dispose();
LayerPropertyTimelineViewModel?.Dispose();
}
}
}

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,4 @@
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Shared.Services;
@ -11,36 +10,11 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
public FolderViewModel(ProfileElement folder,
IProfileEditorService profileEditorService,
IDialogService dialogService,
IRenderElementService renderElementService,
IFolderVmFactory folderVmFactory,
ILayerVmFactory layerVmFactory) :
base(null, folder, profileEditorService, dialogService, renderElementService, folderVmFactory, layerVmFactory)
{
}
public FolderViewModel(TreeItemViewModel parent,
ProfileElement folder,
IProfileEditorService profileEditorService,
IDialogService dialogService,
IRenderElementService renderElementService,
IFolderVmFactory folderVmFactory,
ILayerVmFactory layerVmFactory) :
base(parent, folder, profileEditorService, dialogService, renderElementService, folderVmFactory, layerVmFactory)
IProfileTreeVmFactory profileTreeVmFactory) :
base(folder, profileEditorService, dialogService, profileTreeVmFactory)
{
}
public override bool SupportsChildren => true;
protected override void Dispose(bool disposing)
{
if (disposing)
{
foreach (var treeItemViewModel in Children)
treeItemViewModel.Dispose();
Children.Clear();
}
base.Dispose(disposing);
}
}
}

View File

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

View File

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

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
{
public class ProfileViewModel : ProfileEditorPanelViewModel, IHandle<MainWindowFocusChangedEvent>, IHandle<MainWindowKeyEvent>
public class ProfileViewModel : Screen, IProfileEditorPanelViewModel, IHandle<MainWindowFocusChangedEvent>, IHandle<MainWindowKeyEvent>
{
private readonly IProfileEditorService _profileEditorService;
private readonly IProfileLayerVmFactory _profileLayerVmFactory;

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

View File

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

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

View File

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