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

Profile elements - Simplified class hierarchy

Profile editor - Seperated event types and avoid a lot of type checking
Profile editor - Avoid changing profiles/layers if the new equals the old
Color picker - Fixed binding error
Profile tree - Select first element by default
Profile editor - Adjusted initialization order
This commit is contained in:
Robert 2020-07-09 19:10:04 +02:00
parent a4282006de
commit 94f3d84530
25 changed files with 303 additions and 195 deletions

View File

@ -34,7 +34,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Ben.Demystifier" Version="0.1.6" />
<PackageReference Include="Castle.Core" Version="4.4.1" />
<PackageReference Include="HidSharp" Version="2.1.0" />
<PackageReference Include="LiteDB" Version="5.0.8" />
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.3.0" />

View File

@ -32,5 +32,7 @@ namespace Artemis.Core.Models.Profile.Conditions.Abstract
_children.Remove(displayConditionPart);
}
}
public abstract void ApplyToEntity();
}
}

View File

@ -5,6 +5,12 @@ namespace Artemis.Core.Models.Profile.Conditions
public class DisplayConditionGroup : DisplayConditionPart
{
public BooleanOperator BooleanOperator { get; set; }
public override void ApplyToEntity()
{
foreach (var child in Children)
child.ApplyToEntity();
}
}
public enum BooleanOperator

View File

@ -7,7 +7,7 @@ using SkiaSharp;
namespace Artemis.Core.Models.Profile
{
public sealed class Folder : EffectProfileElement
public sealed class Folder : RenderProfileElement
{
private SKBitmap _folderBitmap;
@ -159,7 +159,7 @@ namespace Artemis.Core.Models.Profile
var path = new SKPath {FillType = SKPathFillType.Winding};
foreach (var child in Children)
{
if (child is EffectProfileElement effectChild && effectChild.Path != null)
if (child is RenderProfileElement effectChild && effectChild.Path != null)
path.AddPath(effectChild.Path);
}
@ -182,10 +182,13 @@ namespace Artemis.Core.Models.Profile
FolderEntity.Enabled = Enabled;
FolderEntity.ProfileId = Profile.EntityId;
FolderEntity.ExpandedPropertyGroups.Clear();
FolderEntity.ExpandedPropertyGroups.AddRange(_expandedPropertyGroups);
ApplyLayerEffectsToEntity();
// TODO: conditions
// Conditions
DisplayConditionGroup?.ApplyToEntity();
}
#region Events

View File

@ -21,9 +21,8 @@ namespace Artemis.Core.Models.Profile
/// Represents a layer on a profile. To create new layers use the <see cref="LayerService" /> by injecting
/// <see cref="ILayerService" /> into your code
/// </summary>
public sealed class Layer : EffectProfileElement
public sealed class Layer : RenderProfileElement
{
private DisplayConditionGroup _displayConditionGroup;
private LayerGeneralProperties _general;
private SKBitmap _layerBitmap;
private BaseLayerBrush _layerBrush;
@ -117,14 +116,7 @@ namespace Artemis.Core.Models.Profile
internal set => SetAndNotify(ref _layerBrush, value);
}
/// <summary>
/// Gets or sets the root display condition group
/// </summary>
public DisplayConditionGroup DisplayConditionGroup
{
get => _displayConditionGroup;
set => SetAndNotify(ref _displayConditionGroup, value);
}
public override string ToString()
{
@ -164,8 +156,8 @@ namespace Artemis.Core.Models.Profile
LayerEntity.Leds.Add(ledEntity);
}
// Conditions TODO
LayerEntity.Conditions.Clear();
// Conditions
DisplayConditionGroup?.ApplyToEntity();
}
#endregion

View File

@ -20,7 +20,7 @@ namespace Artemis.Core.Models.Profile.LayerProperties
/// <summary>
/// Gets the profile element (such as layer or folder) this effect is applied to
/// </summary>
public PropertiesProfileElement ProfileElement { get; internal set; }
public RenderProfileElement ProfileElement { get; internal set; }
/// <summary>
/// The parent group of this layer property, set after construction

View File

@ -31,7 +31,7 @@ namespace Artemis.Core.Models.Profile
/// <summary>
/// Gets the profile element (such as layer or folder) this effect is applied to
/// </summary>
public PropertiesProfileElement ProfileElement { get; internal set; }
public RenderProfileElement ProfileElement { get; internal set; }
/// <summary>
/// The path of this property group
@ -129,7 +129,7 @@ namespace Artemis.Core.Models.Profile
PropertyGroupInitialized?.Invoke(this, EventArgs.Empty);
}
internal void InitializeProperties(ILayerService layerService, PropertiesProfileElement profileElement, [NotNull] string path)
internal void InitializeProperties(ILayerService layerService, RenderProfileElement profileElement, [NotNull] string path)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
@ -236,7 +236,7 @@ namespace Artemis.Core.Models.Profile
OnPropertyGroupOverriding(new PropertyGroupUpdatingEventArgs(overrideTime));
}
private void InitializeProperty(PropertiesProfileElement profileElement, string path, BaseLayerProperty instance)
private void InitializeProperty(RenderProfileElement profileElement, string path, BaseLayerProperty instance)
{
Guid pluginGuid;
if (IsCorePropertyGroup || instance.IsCoreProperty)

View File

@ -6,52 +6,6 @@ namespace Artemis.Core.Models.Profile
{
public abstract class PropertiesProfileElement : ProfileElement
{
private SKPath _path;
internal abstract PropertiesEntity PropertiesEntity { get; }
/// <summary>
/// Gets the path containing all the LEDs this entity is applied to, any rendering outside the entity Path is
/// clipped.
/// </summary>
public SKPath Path
{
get => _path;
protected set
{
SetAndNotify(ref _path, value);
// I can't really be sure about the performance impact of calling Bounds often but
// SkiaSharp calls SkiaApi.sk_path_get_bounds (Handle, &rect); which sounds expensive
Bounds = value?.Bounds ?? SKRect.Empty;
}
}
/// <summary>
/// The bounds of this entity
/// </summary>
public SKRect Bounds
{
get => _bounds;
private set => SetAndNotify(ref _bounds, value);
}
#region Property group expansion
protected List<string> _expandedPropertyGroups;
private SKRect _bounds;
public bool IsPropertyGroupExpanded(LayerPropertyGroup layerPropertyGroup)
{
return _expandedPropertyGroups.Contains(layerPropertyGroup.Path);
}
public void SetPropertyGroupExpanded(LayerPropertyGroup layerPropertyGroup, bool expanded)
{
if (!expanded && IsPropertyGroupExpanded(layerPropertyGroup))
_expandedPropertyGroups.Remove(layerPropertyGroup.Path);
else if (expanded && !IsPropertyGroupExpanded(layerPropertyGroup))
_expandedPropertyGroups.Add(layerPropertyGroup.Path);
}
#endregion
}
}

View File

@ -3,13 +3,69 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Artemis.Core.Annotations;
using Artemis.Core.Models.Profile.Conditions;
using Artemis.Core.Plugins.LayerEffect.Abstract;
using Artemis.Storage.Entities.Profile;
using SkiaSharp;
namespace Artemis.Core.Models.Profile
{
public abstract class EffectProfileElement : PropertiesProfileElement
public abstract class RenderProfileElement : ProfileElement
{
#region Properties
private SKPath _path;
internal abstract PropertiesEntity PropertiesEntity { get; }
/// <summary>
/// Gets the path containing all the LEDs this entity is applied to, any rendering outside the entity Path is
/// clipped.
/// </summary>
public SKPath Path
{
get => _path;
protected set
{
SetAndNotify(ref _path, value);
// I can't really be sure about the performance impact of calling Bounds often but
// SkiaSharp calls SkiaApi.sk_path_get_bounds (Handle, &rect); which sounds expensive
Bounds = value?.Bounds ?? SKRect.Empty;
}
}
/// <summary>
/// The bounds of this entity
/// </summary>
public SKRect Bounds
{
get => _bounds;
private set => SetAndNotify(ref _bounds, value);
}
#region Property group expansion
protected List<string> _expandedPropertyGroups;
private SKRect _bounds;
public bool IsPropertyGroupExpanded(LayerPropertyGroup layerPropertyGroup)
{
return _expandedPropertyGroups.Contains(layerPropertyGroup.Path);
}
public void SetPropertyGroupExpanded(LayerPropertyGroup layerPropertyGroup, bool expanded)
{
if (!expanded && IsPropertyGroupExpanded(layerPropertyGroup))
_expandedPropertyGroups.Remove(layerPropertyGroup.Path);
else if (expanded && !IsPropertyGroupExpanded(layerPropertyGroup))
_expandedPropertyGroups.Add(layerPropertyGroup.Path);
}
#endregion
#endregion
#region Effects
protected List<BaseLayerEffect> _layerEffects;
internal abstract EffectsEntity EffectsEntity { get; }
@ -71,6 +127,23 @@ namespace Artemis.Core.Models.Profile
effect.Dispose();
}
#endregion
#region Conditions
private DisplayConditionGroup _displayConditionGroup;
/// <summary>
/// Gets or sets the root display condition group
/// </summary>
public DisplayConditionGroup DisplayConditionGroup
{
get => _displayConditionGroup;
set => SetAndNotify(ref _displayConditionGroup, value);
}
#endregion
#region Events
public event EventHandler LayerEffectsUpdated;

View File

@ -14,7 +14,7 @@ namespace Artemis.Core.Plugins.LayerEffect.Abstract
public abstract class BaseLayerEffect : PropertyChangedBase, IDisposable
{
private Guid _entityId;
private EffectProfileElement _profileElement;
private RenderProfileElement _profileElement;
private string _name;
private bool _enabled;
private bool _hasBeenRenamed;
@ -33,7 +33,7 @@ namespace Artemis.Core.Plugins.LayerEffect.Abstract
/// <summary>
/// Gets the profile element (such as layer or folder) this effect is applied to
/// </summary>
public EffectProfileElement ProfileElement
public RenderProfileElement ProfileElement
{
get => _profileElement;
internal set => SetAndNotify(ref _profileElement, value);

View File

@ -42,17 +42,17 @@ namespace Artemis.Core.Services.Interfaces
/// Instantiates and adds the <see cref="BaseLayerEffect" /> described by the provided
/// <see cref="LayerEffectDescriptor" /> to the <see cref="Layer" />.
/// </summary>
/// <param name="effectProfileElement">The layer/folder to instantiate the effect for</param>
void InstantiateLayerEffects(EffectProfileElement effectProfileElement);
/// <param name="renderProfileElement">The layer/folder to instantiate the effect for</param>
void InstantiateLayerEffects(RenderProfileElement renderProfileElement);
/// <summary>
/// Adds the <see cref="BaseLayerEffect" /> described by the provided <see cref="LayerEffectDescriptor" /> to the
/// <see cref="Layer" />.
/// </summary>
/// <param name="effectProfileElement">The layer/folder to instantiate the effect for</param>
/// <param name="renderProfileElement">The layer/folder to instantiate the effect for</param>
/// <param name="layerEffectDescriptor"></param>
/// <returns></returns>
BaseLayerEffect AddLayerEffect(EffectProfileElement effectProfileElement, LayerEffectDescriptor layerEffectDescriptor);
BaseLayerEffect AddLayerEffect(RenderProfileElement renderProfileElement, LayerEffectDescriptor layerEffectDescriptor);
void RemoveLayerEffect(BaseLayerEffect layerEffect);
}

View File

@ -20,7 +20,7 @@ namespace Artemis.Core.Services
private readonly ILogger _logger;
private readonly IPluginService _pluginService;
public LayerService(IKernel kernel, ILogger logger, IPluginService pluginService)
public LayerService(IKernel kernel, ILogger logger, IPluginService pluginService, IDataModelService dataModelService)
{
_kernel = kernel;
_logger = logger;
@ -39,7 +39,6 @@ namespace Artemis.Core.Services
// With the properties loaded, the layer brush and effect can be instantiated
InstantiateLayerBrush(layer);
InstantiateLayerEffects(layer);
return layer;
}
@ -85,21 +84,21 @@ namespace Artemis.Core.Services
return brush;
}
public BaseLayerEffect AddLayerEffect(EffectProfileElement effectElement, LayerEffectDescriptor layerEffectDescriptor)
public BaseLayerEffect AddLayerEffect(RenderProfileElement renderElement, LayerEffectDescriptor layerEffectDescriptor)
{
// Create the effect with dependency injection
var effect = (BaseLayerEffect) _kernel.Get(layerEffectDescriptor.LayerEffectType);
effect.ProfileElement = effectElement;
effect.ProfileElement = renderElement;
effect.EntityId = Guid.NewGuid();
effect.Enabled = true;
effect.Order = effectElement.LayerEffects.Count + 1;
effect.Order = renderElement.LayerEffects.Count + 1;
effect.Descriptor = layerEffectDescriptor;
effect.Initialize(this);
effect.Update(0);
effectElement.AddLayerEffect(effect);
renderElement.AddLayerEffect(effect);
_logger.Debug("Added layer effect with root path {rootPath}", effect.PropertyRootPath);
return effect;
}
@ -109,16 +108,16 @@ namespace Artemis.Core.Services
layerEffect.ProfileElement.RemoveLayerEffect(layerEffect);
}
public void InstantiateLayerEffects(EffectProfileElement effectElement)
public void InstantiateLayerEffects(RenderProfileElement renderElement)
{
var layerEffectProviders = _pluginService.GetPluginsOfType<LayerEffectProvider>();
var descriptors = layerEffectProviders.SelectMany(l => l.LayerEffectDescriptors).ToList();
var entities = effectElement.EffectsEntity.LayerEffects.OrderByDescending(e => e.Order).ToList();
var entities = renderElement.EffectsEntity.LayerEffects.OrderByDescending(e => e.Order).ToList();
foreach (var layerEffectEntity in entities)
{
// Skip effects already on the element
if (effectElement.LayerEffects.Any(e => e.EntityId == layerEffectEntity.Id))
if (renderElement.LayerEffects.Any(e => e.EntityId == layerEffectEntity.Id))
continue;
// Get a matching descriptor
@ -130,7 +129,7 @@ namespace Artemis.Core.Services
// Create the effect with dependency injection
var effect = (BaseLayerEffect) _kernel.Get(descriptor.LayerEffectType);
effect.ProfileElement = effectElement;
effect.ProfileElement = renderElement;
effect.EntityId = layerEffectEntity.Id;
effect.Order = layerEffectEntity.Order;
effect.Name = layerEffectEntity.Name;
@ -140,9 +139,14 @@ namespace Artemis.Core.Services
effect.Initialize(this);
effect.Update(0);
effectElement.AddLayerEffect(effect);
renderElement.AddLayerEffect(effect);
_logger.Debug("Instantiated layer effect with root path {rootPath}", effect.PropertyRootPath);
}
}
public void InstantiateDisplayConditions(RenderProfileElement renderElement)
{
}
}
}

View File

@ -4,7 +4,6 @@ namespace Artemis.Storage.Entities.Profile
{
public abstract class PropertiesEntity
{
public List<ProfileConditionEntity> Conditions { get; set; }
public List<PropertyEntity> PropertyEntities { get; set; }
public List<string> ExpandedPropertyGroups { get; set; }
}

View File

@ -90,7 +90,7 @@
<Popup AllowsTransparency="True"
Placement="Bottom"
CustomPopupPlacementCallback="{x:Static materialDesign:CustomPopupPlacementCallbackHelper.LargePopupCallback}"
PlacementTarget="{Binding ElementName=PART_TextBox}"
PlacementTarget="{Binding ElementName=ColorCodeTextBox}"
StaysOpen="False"
PopupAnimation="Fade"
IsOpen="{Binding PopupOpen, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}">

View File

@ -1,22 +0,0 @@
using System;
using Artemis.Core.Models.Profile;
namespace Artemis.UI.Shared.Events
{
public class ProfileElementEventArgs : EventArgs
{
public ProfileElementEventArgs(ProfileElement profileElement)
{
ProfileElement = profileElement;
}
public ProfileElementEventArgs(ProfileElement profileElement, ProfileElement previousProfileElement)
{
ProfileElement = profileElement;
PreviousProfileElement = previousProfileElement;
}
public ProfileElement ProfileElement { get; }
public ProfileElement PreviousProfileElement { get; }
}
}

View File

@ -0,0 +1,22 @@
using System;
using Artemis.Core.Models.Profile;
namespace Artemis.UI.Shared.Events
{
public class ProfileEventArgs : EventArgs
{
public ProfileEventArgs(Profile profile)
{
Profile = profile;
}
public ProfileEventArgs(Profile profile, Profile previousProfile)
{
Profile = profile;
PreviousProfile = previousProfile;
}
public Profile Profile { get; }
public Profile PreviousProfile { get; }
}
}

View File

@ -0,0 +1,22 @@
using System;
using Artemis.Core.Models.Profile;
namespace Artemis.UI.Shared.Events
{
public class RenderProfileElementEventArgs : EventArgs
{
public RenderProfileElementEventArgs(RenderProfileElement renderProfileElement)
{
RenderProfileElement = renderProfileElement;
}
public RenderProfileElementEventArgs(RenderProfileElement renderProfileElement, RenderProfileElement previousRenderProfileElement)
{
RenderProfileElement = renderProfileElement;
PreviousRenderProfileElement = previousRenderProfileElement;
}
public RenderProfileElement RenderProfileElement { get; }
public RenderProfileElement PreviousRenderProfileElement { get; }
}
}

View File

@ -12,7 +12,7 @@ namespace Artemis.UI.Shared.Services.Interfaces
public interface IProfileEditorService : IArtemisSharedUIService
{
Profile SelectedProfile { get; }
ProfileElement SelectedProfileElement { get; }
RenderProfileElement SelectedProfileElement { get; }
TimeSpan CurrentTime { get; set; }
int PixelsPerSecond { get; set; }
IReadOnlyList<PropertyInputRegistration> RegisteredPropertyEditors { get; }
@ -20,7 +20,7 @@ namespace Artemis.UI.Shared.Services.Interfaces
void ChangeSelectedProfile(Profile profile);
void UpdateSelectedProfile();
void ChangeSelectedProfileElement(ProfileElement profileElement);
void ChangeSelectedProfileElement(RenderProfileElement profileElement);
void UpdateSelectedProfileElement();
void UpdateProfilePreview();
void UndoUpdateProfile(ProfileModule module);
@ -32,22 +32,22 @@ namespace Artemis.UI.Shared.Services.Interfaces
/// <summary>
/// Occurs when a new profile is selected
/// </summary>
event EventHandler<ProfileElementEventArgs> ProfileSelected;
event EventHandler<ProfileEventArgs> ProfileSelected;
/// <summary>
/// Occurs then the currently selected profile is updated
/// </summary>
event EventHandler<ProfileElementEventArgs> SelectedProfileUpdated;
event EventHandler<ProfileEventArgs> SelectedProfileUpdated;
/// <summary>
/// Occurs when a new profile element is selected
/// </summary>
event EventHandler<ProfileElementEventArgs> ProfileElementSelected;
event EventHandler<RenderProfileElementEventArgs> ProfileElementSelected;
/// <summary>
/// Occurs when the currently selected profile element is updated
/// </summary>
event EventHandler<ProfileElementEventArgs> SelectedProfileElementUpdated;
event EventHandler<RenderProfileElementEventArgs> SelectedProfileElementUpdated;
/// <summary>
/// Occurs when the current editor time is changed

View File

@ -11,6 +11,7 @@ using Artemis.UI.Shared.Events;
using Artemis.UI.Shared.PropertyInput;
using Artemis.UI.Shared.Services.Interfaces;
using Ninject;
using Serilog;
namespace Artemis.UI.Shared.Services
{
@ -18,15 +19,17 @@ namespace Artemis.UI.Shared.Services
{
private readonly ICoreService _coreService;
private readonly IProfileService _profileService;
private readonly ILogger _logger;
private readonly List<PropertyInputRegistration> _registeredPropertyEditors;
private TimeSpan _currentTime;
private TimeSpan _lastUpdateTime;
private int _pixelsPerSecond;
public ProfileEditorService(ICoreService coreService, IProfileService profileService, IKernel kernel)
public ProfileEditorService(ICoreService coreService, IProfileService profileService, IKernel kernel, ILogger logger)
{
_coreService = coreService;
_profileService = profileService;
_logger = logger;
_registeredPropertyEditors = new List<PropertyInputRegistration>();
Kernel = kernel;
@ -36,7 +39,7 @@ namespace Artemis.UI.Shared.Services
public IKernel Kernel { get; }
public IReadOnlyList<PropertyInputRegistration> RegisteredPropertyEditors => _registeredPropertyEditors.AsReadOnly();
public Profile SelectedProfile { get; private set; }
public ProfileElement SelectedProfileElement { get; private set; }
public RenderProfileElement SelectedProfileElement { get; private set; }
public TimeSpan CurrentTime
{
@ -63,9 +66,13 @@ namespace Artemis.UI.Shared.Services
public void ChangeSelectedProfile(Profile profile)
{
if (SelectedProfile == profile)
return;
_logger.Verbose("ChangeSelectedProfile {profile}", profile);
ChangeSelectedProfileElement(null);
var profileElementEvent = new ProfileElementEventArgs(profile, SelectedProfile);
var profileElementEvent = new ProfileEventArgs(profile, SelectedProfile);
SelectedProfile = profile;
UpdateProfilePreview();
OnSelectedProfileChanged(profileElementEvent);
@ -73,23 +80,29 @@ namespace Artemis.UI.Shared.Services
public void UpdateSelectedProfile()
{
_logger.Verbose("UpdateSelectedProfile {profile}", SelectedProfile);
_profileService.UpdateProfile(SelectedProfile, true);
UpdateProfilePreview();
OnSelectedProfileElementUpdated(new ProfileElementEventArgs(SelectedProfile));
OnSelectedProfileChanged(new ProfileEventArgs(SelectedProfile));
}
public void ChangeSelectedProfileElement(ProfileElement profileElement)
public void ChangeSelectedProfileElement(RenderProfileElement profileElement)
{
var profileElementEvent = new ProfileElementEventArgs(profileElement, SelectedProfileElement);
if (SelectedProfileElement == profileElement)
return;
_logger.Verbose("ChangeSelectedProfileElement {profile}", profileElement);
var profileElementEvent = new RenderProfileElementEventArgs(profileElement, SelectedProfileElement);
SelectedProfileElement = profileElement;
OnSelectedProfileElementChanged(profileElementEvent);
}
public void UpdateSelectedProfileElement()
{
_logger.Verbose("UpdateSelectedProfileElement {profile}", SelectedProfileElement);
_profileService.UpdateProfile(SelectedProfile, true);
UpdateProfilePreview();
OnSelectedProfileElementUpdated(new ProfileElementEventArgs(SelectedProfileElement));
OnSelectedProfileElementUpdated(new RenderProfileElementEventArgs(SelectedProfileElement));
}
public void UpdateProfilePreview()
@ -122,11 +135,11 @@ namespace Artemis.UI.Shared.Services
if (!undid)
return;
OnSelectedProfileChanged(new ProfileElementEventArgs(SelectedProfile, SelectedProfile));
OnSelectedProfileChanged(new ProfileEventArgs(SelectedProfile, SelectedProfile));
if (SelectedProfileElement != null)
{
var elements = SelectedProfile.GetAllLayers().Cast<ProfileElement>().ToList();
var elements = SelectedProfile.GetAllLayers().Cast<RenderProfileElement>().ToList();
elements.AddRange(SelectedProfile.GetAllFolders());
var element = elements.FirstOrDefault(l => l.EntityId == SelectedProfileElement.EntityId);
ChangeSelectedProfileElement(element);
@ -141,11 +154,11 @@ namespace Artemis.UI.Shared.Services
if (!redid)
return;
OnSelectedProfileChanged(new ProfileElementEventArgs(SelectedProfile, SelectedProfile));
OnSelectedProfileChanged(new ProfileEventArgs(SelectedProfile, SelectedProfile));
if (SelectedProfileElement != null)
{
var elements = SelectedProfile.GetAllLayers().Cast<ProfileElement>().ToList();
var elements = SelectedProfile.GetAllLayers().Cast<RenderProfileElement>().ToList();
elements.AddRange(SelectedProfile.GetAllFolders());
var element = elements.FirstOrDefault(l => l.EntityId == SelectedProfileElement.EntityId);
ChangeSelectedProfileElement(element);
@ -194,10 +207,10 @@ namespace Artemis.UI.Shared.Services
return (Module) SelectedProfile?.PluginInfo.Instance;
}
public event EventHandler<ProfileElementEventArgs> ProfileSelected;
public event EventHandler<ProfileElementEventArgs> SelectedProfileUpdated;
public event EventHandler<ProfileElementEventArgs> ProfileElementSelected;
public event EventHandler<ProfileElementEventArgs> SelectedProfileElementUpdated;
public event EventHandler<ProfileEventArgs> ProfileSelected;
public event EventHandler<ProfileEventArgs> SelectedProfileUpdated;
public event EventHandler<RenderProfileElementEventArgs> ProfileElementSelected;
public event EventHandler<RenderProfileElementEventArgs> SelectedProfileElementUpdated;
public event EventHandler CurrentTimeChanged;
public event EventHandler PixelsPerSecondChanged;
public event EventHandler ProfilePreviewUpdated;
@ -212,22 +225,22 @@ namespace Artemis.UI.Shared.Services
_coreService.PluginUpdatingDisabled = false;
}
protected virtual void OnSelectedProfileChanged(ProfileElementEventArgs e)
protected virtual void OnSelectedProfileChanged(ProfileEventArgs e)
{
ProfileSelected?.Invoke(this, e);
}
protected virtual void OnSelectedProfileUpdated(ProfileElementEventArgs e)
protected virtual void OnSelectedProfileUpdated(ProfileEventArgs e)
{
SelectedProfileUpdated?.Invoke(this, e);
}
protected virtual void OnSelectedProfileElementChanged(ProfileElementEventArgs e)
protected virtual void OnSelectedProfileElementChanged(RenderProfileElementEventArgs e)
{
ProfileElementSelected?.Invoke(this, e);
}
protected virtual void OnSelectedProfileElementUpdated(ProfileElementEventArgs e)
protected virtual void OnSelectedProfileElementUpdated(RenderProfileElementEventArgs e)
{
SelectedProfileElementUpdated?.Invoke(this, e);
}

View File

@ -123,7 +123,6 @@
<Resource Include="Resources\Cursors\aero_rotate.cur" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Castle.Core" Version="4.4.1" />
<PackageReference Include="FluentValidation" Version="8.6.2" />
<PackageReference Include="gong-wpf-dragdrop" Version="2.2.0" />
<PackageReference Include="Hardcodet.NotifyIcon.Wpf.NetCore" Version="1.0.11" />

View File

@ -1,5 +1,4 @@
using Artemis.Core.Models.Profile;
using Artemis.Core.Models.Profile.Conditions;
using Artemis.Core.Models.Profile.Conditions;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Shared.Events;
using Artemis.UI.Shared.Services.Interfaces;
@ -23,20 +22,21 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
set => SetAndNotify(ref _rootGroup, value);
}
private void ProfileEditorServiceOnProfileElementSelected(object sender, ProfileElementEventArgs e)
private void ProfileEditorServiceOnProfileElementSelected(object sender, RenderProfileElementEventArgs e)
{
if (e.ProfileElement is Layer layer)
if (e.RenderProfileElement == null)
{
// Ensure the layer has a root display condition group
if (layer.DisplayConditionGroup == null)
layer.DisplayConditionGroup = new DisplayConditionGroup();
RootGroup = _displayConditionsVmFactory.DisplayConditionGroupViewModel(layer.DisplayConditionGroup, null);
RootGroup.IsRootGroup = true;
RootGroup.Update();
}
else
RootGroup = null;
return;
}
// Ensure the layer has a root display condition group
if (e.RenderProfileElement.DisplayConditionGroup == null)
e.RenderProfileElement.DisplayConditionGroup = new DisplayConditionGroup();
RootGroup = _displayConditionsVmFactory.DisplayConditionGroupViewModel(e.RenderProfileElement.DisplayConditionGroup, null);
RootGroup.IsRootGroup = true;
RootGroup.Update();
}
}
}

View File

@ -56,11 +56,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.LayerEffects
private void HandleSelectedLayerEffectChanged(object sender, PropertyChangedEventArgs e)
{
EffectProfileElement effectElement;
RenderProfileElement renderElement;
if (LayerPropertiesViewModel.SelectedLayer != null)
effectElement = LayerPropertiesViewModel.SelectedLayer;
renderElement = LayerPropertiesViewModel.SelectedLayer;
else if (LayerPropertiesViewModel.SelectedFolder != null)
effectElement = LayerPropertiesViewModel.SelectedFolder;
renderElement = LayerPropertiesViewModel.SelectedFolder;
else
return;
@ -70,7 +70,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.LayerEffects
Execute.PostToUIThread(async () =>
{
await Task.Delay(500);
_layerService.AddLayerEffect(effectElement, SelectedLayerEffectDescriptor);
_layerService.AddLayerEffect(renderElement, SelectedLayerEffectDescriptor);
_profileEditorService.UpdateSelectedProfileElement();
});
}

View File

@ -29,7 +29,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
private LayerPropertyGroupViewModel _brushPropertyGroup;
private bool _repeatAfterLastKeyframe;
private int _propertyTreeIndex;
private PropertiesProfileElement _selectedPropertiesElement;
private RenderProfileElement _selectedProfileElement;
private BindableCollection<LayerPropertyGroupViewModel> _layerPropertyGroups;
private TreeViewModel _treeViewModel;
private EffectsViewModel _effectsViewModel;
@ -86,19 +86,19 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
public bool PropertyTreeVisible => PropertyTreeIndex == 0;
public PropertiesProfileElement SelectedPropertiesElement
public RenderProfileElement SelectedProfileElement
{
get => _selectedPropertiesElement;
get => _selectedProfileElement;
set
{
if (!SetAndNotify(ref _selectedPropertiesElement, value)) return;
if (!SetAndNotify(ref _selectedProfileElement, value)) return;
NotifyOfPropertyChange(nameof(SelectedLayer));
NotifyOfPropertyChange(nameof(SelectedFolder));
}
}
public Layer SelectedLayer => SelectedPropertiesElement as Layer;
public Folder SelectedFolder => SelectedPropertiesElement as Folder;
public Layer SelectedLayer => SelectedProfileElement as Layer;
public Folder SelectedFolder => SelectedProfileElement as Folder;
public BindableCollection<LayerPropertyGroupViewModel> LayerPropertyGroups
{
@ -126,8 +126,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
protected override void OnInitialActivate()
{
if (ProfileEditorService.SelectedProfileElement is PropertiesProfileElement propertiesElement)
PopulateProperties(propertiesElement);
PopulateProperties(ProfileEditorService.SelectedProfileElement);
ProfileEditorService.ProfileElementSelected += ProfileEditorServiceOnProfileElementSelected;
ProfileEditorService.CurrentTimeChanged += ProfileEditorServiceOnCurrentTimeChanged;
@ -146,12 +145,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
base.OnClose();
}
protected override void OnActivate()
{
PopulateProperties(ProfileEditorService.SelectedProfileElement as PropertiesProfileElement);
base.OnActivate();
}
protected override void OnDeactivate()
{
Pause();
@ -164,12 +157,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
EffectsViewModel.PopulateDescriptors();
}
private void ProfileEditorServiceOnProfileElementSelected(object sender, ProfileElementEventArgs e)
private void ProfileEditorServiceOnProfileElementSelected(object sender, RenderProfileElementEventArgs e)
{
PopulateProperties(e.ProfileElement as PropertiesProfileElement);
PopulateProperties(e.RenderProfileElement);
}
private void ProfileEditorServiceOnCurrentTimeChanged(object sender, EventArgs e)
{
NotifyOfPropertyChange(nameof(FormattedCurrentTime));
@ -190,23 +182,27 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
return groups;
}
private void PopulateProperties(PropertiesProfileElement profileElement)
private void PopulateProperties(RenderProfileElement profileElement)
{
if (SelectedPropertiesElement != null && SelectedPropertiesElement is EffectProfileElement effectElement)
effectElement.LayerEffectsUpdated -= SelectedElementOnLayerEffectsUpdated;
// Unsubscribe from old selected element
if (SelectedProfileElement != null)
SelectedProfileElement.LayerEffectsUpdated -= SelectedElementOnLayerEffectsUpdated;
if (SelectedLayer != null)
SelectedLayer.LayerBrushUpdated -= SelectedLayerOnLayerBrushUpdated;
// Clear old properties
foreach (var layerPropertyGroupViewModel in LayerPropertyGroups)
layerPropertyGroupViewModel.Dispose();
LayerPropertyGroups.Clear();
_brushPropertyGroup = null;
SelectedPropertiesElement = profileElement;
if (SelectedPropertiesElement is EffectProfileElement newEffectElement)
newEffectElement.LayerEffectsUpdated += SelectedElementOnLayerEffectsUpdated;
if (profileElement == null)
return;
// Subscribe to new element
SelectedProfileElement = profileElement;
SelectedProfileElement.LayerEffectsUpdated += SelectedElementOnLayerEffectsUpdated;
// Apply layer properties
if (SelectedLayer != null)
{
SelectedLayer.LayerBrushUpdated += SelectedLayerOnLayerBrushUpdated;
@ -278,21 +274,21 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
private void ApplyEffects()
{
EffectProfileElement effectElement;
RenderProfileElement renderElement;
if (SelectedLayer != null)
effectElement = SelectedLayer;
renderElement = SelectedLayer;
else if (SelectedFolder != null)
effectElement = SelectedFolder;
renderElement = SelectedFolder;
else
return;
// Remove VMs of effects no longer applied on the layer
var toRemove = LayerPropertyGroups.Where(l => l.LayerPropertyGroup.LayerEffect != null && !effectElement.LayerEffects.Contains(l.LayerPropertyGroup.LayerEffect)).ToList();
var toRemove = LayerPropertyGroups.Where(l => l.LayerPropertyGroup.LayerEffect != null && !renderElement.LayerEffects.Contains(l.LayerPropertyGroup.LayerEffect)).ToList();
LayerPropertyGroups.RemoveRange(toRemove);
foreach (var layerPropertyGroupViewModel in toRemove)
layerPropertyGroupViewModel.Dispose();
foreach (var layerEffect in effectElement.LayerEffects)
foreach (var layerEffect in renderElement.LayerEffects)
{
if (LayerPropertyGroups.Any(l => l.LayerPropertyGroup.LayerEffect == layerEffect))
continue;

View File

@ -28,6 +28,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor
private PluginSetting<GridLength> _displayConditionsHeight;
private PluginSetting<GridLength> _bottomPanelsHeight;
private PluginSetting<GridLength> _elementPropertiesWidth;
private ProfileViewModel _profileViewModel;
private ProfileTreeViewModel _profileTreeViewModel;
private LayerPropertiesViewModel _layerPropertiesViewModel;
private DisplayConditionsViewModel _displayConditionsViewModel;
public ProfileEditorViewModel(ProfileModule module,
ICollection<ProfileEditorPanelViewModel> viewModels,
@ -44,21 +48,44 @@ namespace Artemis.UI.Screens.Module.ProfileEditor
Module = module;
DialogService = dialogService;
DisplayConditionsViewModel = (DisplayConditionsViewModel) viewModels.First(vm => vm is DisplayConditionsViewModel);
LayerPropertiesViewModel = (LayerPropertiesViewModel) viewModels.First(vm => vm is LayerPropertiesViewModel);
ProfileTreeViewModel = (ProfileTreeViewModel) viewModels.First(vm => vm is ProfileTreeViewModel);
ProfileViewModel = (ProfileViewModel) viewModels.First(vm => vm is ProfileViewModel);
Profiles = new BindableCollection<Profile>();
// Run this first to let VMs activate without causing constant UI updates
Items.AddRange(viewModels);
// Populate the panels
ProfileViewModel = (ProfileViewModel) viewModels.First(vm => vm is ProfileViewModel);
ProfileTreeViewModel = (ProfileTreeViewModel) viewModels.First(vm => vm is ProfileTreeViewModel);
DisplayConditionsViewModel = (DisplayConditionsViewModel) viewModels.First(vm => vm is DisplayConditionsViewModel);
LayerPropertiesViewModel = (LayerPropertiesViewModel) viewModels.First(vm => vm is LayerPropertiesViewModel);
}
public ProfileModule Module { get; }
public IDialogService DialogService { get; }
public DisplayConditionsViewModel DisplayConditionsViewModel { get; }
public LayerPropertiesViewModel LayerPropertiesViewModel { get; }
public ProfileTreeViewModel ProfileTreeViewModel { get; }
public ProfileViewModel ProfileViewModel { get; }
public DisplayConditionsViewModel DisplayConditionsViewModel
{
get => _displayConditionsViewModel;
set => SetAndNotify(ref _displayConditionsViewModel, value);
}
public LayerPropertiesViewModel LayerPropertiesViewModel
{
get => _layerPropertiesViewModel;
set => SetAndNotify(ref _layerPropertiesViewModel, value);
}
public ProfileTreeViewModel ProfileTreeViewModel
{
get => _profileTreeViewModel;
set => SetAndNotify(ref _profileTreeViewModel, value);
}
public ProfileViewModel ProfileViewModel
{
get => _profileViewModel;
set => SetAndNotify(ref _profileViewModel, value);
}
public BindableCollection<Profile> Profiles
{

View File

@ -1,11 +1,13 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using Artemis.Core.Models.Profile;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem;
using Artemis.UI.Shared.Services.Interfaces;
using GongSolutions.Wpf.DragDrop;
using Stylet;
namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree
{
@ -21,10 +23,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree
{
_profileEditorService = profileEditorService;
_folderVmFactory = folderVmFactory;
CreateRootFolderViewModel();
_profileEditorService.ProfileSelected += OnProfileSelected;
_profileEditorService.ProfileElementSelected += OnProfileElementSelected;
}
public FolderViewModel RootFolder
@ -39,8 +37,12 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree
set
{
if (_updatingTree) return;
SetAndNotify(ref _selectedTreeItem, value);
_profileEditorService.ChangeSelectedProfileElement(value?.ProfileElement);
if (!SetAndNotify(ref _selectedTreeItem, value)) return;
if (value != null && value.ProfileElement is RenderProfileElement renderElement)
_profileEditorService.ChangeSelectedProfileElement(renderElement);
else
_profileEditorService.ChangeSelectedProfileElement(null);
}
}
@ -109,6 +111,13 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree
RootFolder = _folderVmFactory.Create(folder);
_updatingTree = false;
// Auto-select the first layer
if (_profileEditorService.SelectedProfile != null && SelectedTreeItem == null)
{
if (_profileEditorService.SelectedProfile.GetRootFolder().Children.FirstOrDefault() is RenderProfileElement firstElement)
Execute.PostToUIThread(() => _profileEditorService.ChangeSelectedProfileElement(firstElement));
}
}
private static DragDropType GetDragDropType(IDropInfo dropInfo)
@ -140,8 +149,18 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree
}
}
protected override void OnInitialActivate()
{
_profileEditorService.ProfileSelected += OnProfileSelected;
_profileEditorService.ProfileElementSelected += OnProfileElementSelected;
CreateRootFolderViewModel();
}
protected override void OnClose()
{
_profileEditorService.ProfileSelected -= OnProfileSelected;
_profileEditorService.ProfileElementSelected -= OnProfileElementSelected;
RootFolder?.Dispose();
RootFolder = null;
base.OnClose();