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

Profiles - Only load nodes after the entire profile structure loaded

Profiles - Added API to get all render elements
Nodes - Added layer property node
This commit is contained in:
Robert 2021-08-21 13:51:25 +02:00
parent 836e979991
commit a4667fdc03
11 changed files with 317 additions and 57 deletions

View File

@ -242,7 +242,11 @@ namespace Artemis.Core
EasingTime = Entity.EasingTime;
EasingFunction = (Easings.Functions) Entity.EasingFunction;
}
/// <inheritdoc />
public void LoadNodeScript()
{
Script.Dispose();
Script = Entity.NodeScript != null
? new NodeScript<TProperty>(GetScriptName(), "The value to put into the data binding", Entity.NodeScript, LayerProperty.ProfileElement.Profile)

View File

@ -67,7 +67,7 @@ namespace Artemis.Core
DataBinding = new DataBinding<TLayerProperty, TProperty>(LayerProperty, dataBinding);
return DataBinding;
}
/// <inheritdoc />
public void ClearDataBinding()
{

View File

@ -19,5 +19,10 @@ namespace Artemis.Core
/// Applies the data binding to the layer property
/// </summary>
void Apply();
/// <summary>
/// If the data binding is enabled, loads the node script for that data binding
/// </summary>
void LoadNodeScript();
}
}

View File

@ -206,12 +206,10 @@ namespace Artemis.Core
}
}
List<RenderProfileElement> renderElements = GetAllRenderElements();
if (ProfileEntity.LastSelectedProfileElement != Guid.Empty)
{
LastSelectedProfileElement = GetAllFolders().FirstOrDefault(f => f.EntityId == ProfileEntity.LastSelectedProfileElement);
if (LastSelectedProfileElement == null)
LastSelectedProfileElement = GetAllLayers().FirstOrDefault(f => f.EntityId == ProfileEntity.LastSelectedProfileElement);
}
LastSelectedProfileElement = renderElements.FirstOrDefault(f => f.EntityId == ProfileEntity.LastSelectedProfileElement);
else
LastSelectedProfileElement = null;
@ -219,6 +217,10 @@ namespace Artemis.Core
scriptConfiguration.Script?.Dispose();
ScriptConfigurations.Clear();
ScriptConfigurations.AddRange(ProfileEntity.ScriptConfigurations.Select(e => new ScriptConfiguration(e)));
// Load node scripts last since they may rely on the profile structure being in place
foreach (RenderProfileElement renderProfileElement in renderElements)
renderProfileElement.LoadNodeScript();
}
internal override void Save()
@ -253,10 +255,7 @@ namespace Artemis.Core
/// <inheritdoc />
public override IEnumerable<IBreakableModel> GetBrokenHierarchy()
{
foreach (IBreakableModel breakableModel in GetAllFolders().SelectMany(folders => folders.GetBrokenHierarchy()))
yield return breakableModel;
foreach (IBreakableModel breakableModel in GetAllLayers().SelectMany(layer => layer.GetBrokenHierarchy()))
yield return breakableModel;
return GetAllRenderElements().SelectMany(folders => folders.GetBrokenHierarchy());
}
#endregion

View File

@ -99,6 +99,13 @@ namespace Artemis.Core
/// </summary>
public bool Disposed { get; protected set; }
#region Overrides of BreakableModel
/// <inheritdoc />
public override string BrokenDisplayName => Name ?? GetType().Name;
#endregion
/// <summary>
/// Updates the element
/// </summary>
@ -121,13 +128,6 @@ namespace Artemis.Core
return $"{nameof(EntityId)}: {EntityId}, {nameof(Order)}: {Order}, {nameof(Name)}: {Name}";
}
#region Overrides of BreakableModel
/// <inheritdoc />
public override string BrokenDisplayName => Name ?? GetType().Name;
#endregion
#region Hierarchy
/// <summary>
@ -147,7 +147,9 @@ namespace Artemis.Core
// Add to the end of the list
if (order == null)
{
ChildrenList.Add(child);
}
// Insert at the given index
else
{
@ -191,6 +193,27 @@ namespace Artemis.Core
ChildrenList[index].Order = index;
}
/// <summary>
/// Returns a flattened list of all child render elements
/// </summary>
/// <returns></returns>
public List<RenderProfileElement> GetAllRenderElements()
{
if (Disposed)
throw new ObjectDisposedException(GetType().Name);
List<RenderProfileElement> elements = new();
foreach (RenderProfileElement childElement in Children.Where(c => c is RenderProfileElement).Cast<RenderProfileElement>())
{
// Add all folders in this element
elements.Add(childElement);
// Add all folders in folders inside this element
elements.AddRange(childElement.GetAllRenderElements());
}
return elements;
}
/// <summary>
/// Returns a flattened list of all child folders
/// </summary>
@ -240,27 +263,6 @@ namespace Artemis.Core
internal abstract void Load();
internal abstract void Save();
#endregion
#region IDisposable
/// <inheritdoc />
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Disposes the profile element
/// </summary>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
}
}
#endregion
#region Events
@ -292,5 +294,26 @@ namespace Artemis.Core
}
#endregion
#region IDisposable
/// <inheritdoc />
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Disposes the profile element
/// </summary>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
}
}
#endregion
}
}

View File

@ -70,10 +70,6 @@ namespace Artemis.Core
internal void LoadRenderElement()
{
DisplayCondition = RenderElementEntity.NodeScript != null
? new NodeScript<bool>($"Activate {_typeDisplayName}", $"Whether or not this {_typeDisplayName} should be active", RenderElementEntity.NodeScript, Profile)
: new NodeScript<bool>($"Activate {_typeDisplayName}", $"Whether or not this {_typeDisplayName} should be active", Profile);
Timeline = RenderElementEntity.Timeline != null
? new Timeline(RenderElementEntity.Timeline)
: new Timeline();
@ -111,6 +107,19 @@ namespace Artemis.Core
Timeline?.Save();
}
internal void LoadNodeScript()
{
DisplayCondition = RenderElementEntity.NodeScript != null
? new NodeScript<bool>($"Activate {_typeDisplayName}", $"Whether or not this {_typeDisplayName} should be active", RenderElementEntity.NodeScript, Profile)
: new NodeScript<bool>($"Activate {_typeDisplayName}", $"Whether or not this {_typeDisplayName} should be active", Profile);
foreach (ILayerProperty layerProperty in GetAllLayerProperties())
{
foreach (IDataBindingRegistration dataBindingRegistration in layerProperty.GetAllDataBindingRegistrations())
dataBindingRegistration.GetDataBinding()?.LoadNodeScript();
}
}
internal void OnLayerEffectsUpdated()
{
LayerEffectsUpdated?.Invoke(this, EventArgs.Empty);

View File

@ -438,10 +438,8 @@ namespace Artemis.Core.Services
profile.Save();
if (includeChildren)
{
foreach (Folder folder in profile.GetAllFolders())
folder.Save();
foreach (Layer layer in profile.GetAllLayers())
layer.Save();
foreach (RenderProfileElement child in profile.GetAllRenderElements())
child.Save();
}
// If there are no changes, don't bother saving
@ -599,11 +597,9 @@ namespace Artemis.Core.Services
profile.Save();
foreach (Folder folder in profile.GetAllFolders())
folder.Save();
foreach (Layer layer in profile.GetAllLayers())
layer.Save();
foreach (RenderProfileElement renderProfileElement in profile.GetAllRenderElements())
renderProfileElement.Save();
_logger.Debug("Adapt profile - Saving " + profile);
profile.RedoStack.Clear();
profile.UndoStack.Push(memento);

View File

@ -313,15 +313,11 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
if (SuspendedEditing || !_settingsService.GetSetting("ProfileEditor.AlwaysApplyDataBindings", true).Value || _profileEditorService.SelectedProfile == null)
return;
foreach (IDataBindingRegistration dataBindingRegistration in _profileEditorService.SelectedProfile.GetAllFolders()
foreach (IDataBindingRegistration dataBindingRegistration in _profileEditorService.SelectedProfile.GetAllRenderElements()
.SelectMany(f => f.GetAllLayerProperties(), (f, p) => p)
.SelectMany(p => p.GetAllDataBindingRegistrations()))
dataBindingRegistration.GetDataBinding()?.UpdateWithDelta(delta);
foreach (IDataBindingRegistration dataBindingRegistration in _profileEditorService.SelectedProfile.GetAllLayers()
.SelectMany(f => f.GetAllLayerProperties(), (f, p) => p)
.SelectMany(p => p.GetAllDataBindingRegistrations()))
dataBindingRegistration.GetDataBinding()?.UpdateWithDelta(delta);
// TODO: Only update when there are data bindings
if (!_profileEditorService.Playing)
_profileEditorService.UpdateProfilePreview();

View File

@ -0,0 +1,80 @@
using System.Collections.Generic;
using System.Linq;
using Artemis.Core;
using Stylet;
namespace Artemis.VisualScripting.Nodes.CustomViewModels
{
public class LayerPropertyNodeCustomViewModel : CustomNodeViewModel
{
private readonly LayerPropertyNode _node;
private RenderProfileElement _selectedProfileElement;
private ILayerProperty _selectedLayerProperty;
public LayerPropertyNodeCustomViewModel(LayerPropertyNode node) : base(node)
{
_node = node;
}
public BindableCollection<RenderProfileElement> ProfileElements { get; } = new();
public RenderProfileElement SelectedProfileElement
{
get => _selectedProfileElement;
set
{
if (!SetAndNotify(ref _selectedProfileElement, value)) return;
_node.ChangeProfileElement(_selectedProfileElement);
GetLayerProperties();
}
}
public BindableCollection<ILayerProperty> LayerProperties { get; } = new();
public ILayerProperty SelectedLayerProperty
{
get => _selectedLayerProperty;
set
{
if (!SetAndNotify(ref _selectedLayerProperty, value)) return;
_node.ChangeLayerProperty(_selectedLayerProperty);
}
}
private void GetProfileElements()
{
ProfileElements.Clear();
if (_node.Script.Context is not Profile profile)
return;
List<RenderProfileElement> elements = new(profile.GetAllRenderElements());
ProfileElements.AddRange(elements.OrderBy(e => e.Order));
_selectedProfileElement = _node.ProfileElement;
NotifyOfPropertyChange(nameof(SelectedProfileElement));
}
private void GetLayerProperties()
{
LayerProperties.Clear();
if (_node.ProfileElement == null)
return;
LayerProperties.AddRange(_node.ProfileElement.GetAllLayerProperties().Where(l => l.GetAllDataBindingRegistrations().Any()));
_selectedLayerProperty = _node.LayerProperty;
NotifyOfPropertyChange(nameof(SelectedLayerProperty));
}
#region Overrides of CustomNodeViewModel
/// <inheritdoc />
protected override void OnDisplay()
{
GetProfileElements();
GetLayerProperties();
}
#endregion
}
}

View File

@ -0,0 +1,18 @@
<UserControl x:Class="Artemis.VisualScripting.Nodes.CustomViews.LayerPropertyNodeCustomView"
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:local="clr-namespace:Artemis.VisualScripting.Nodes.CustomViews"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel>
<ComboBox Margin="8 0"
ItemsSource="{Binding ProfileElements}"
SelectedValue="{Binding SelectedProfileElement}" />
<ComboBox Margin="8 0"
ItemsSource="{Binding LayerProperties}"
SelectedValue="{Binding SelectedLayerProperty}"
DisplayMemberPath="PropertyDescription.Name"/>
</StackPanel>
</UserControl>

View File

@ -0,0 +1,130 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Artemis.Core;
using Artemis.VisualScripting.Nodes.CustomViewModels;
namespace Artemis.VisualScripting.Nodes
{
[Node("Layer/Folder Property", "Outputs the property of a selected layer or folder")]
public class LayerPropertyNode : Node<LayerPropertyNodeCustomViewModel>
{
private readonly object _layerPropertyLock = new();
public INodeScript Script { get; private set; }
public RenderProfileElement ProfileElement { get; private set; }
public ILayerProperty LayerProperty { get; private set; }
public override void Evaluate()
{
lock (_layerPropertyLock)
{
// In this case remove the pins so no further evaluations occur
if (LayerProperty == null)
{
CreatePins();
return;
}
List<IDataBindingRegistration> list = LayerProperty.GetAllDataBindingRegistrations();
int index = 0;
foreach (IPin pin in Pins)
{
OutputPin outputPin = (OutputPin) pin;
IDataBindingRegistration dataBindingRegistration = list[index];
index++;
// TODO: Is this really non-nullable?
outputPin.Value = dataBindingRegistration.GetValue();
}
}
}
public override void Initialize(INodeScript script)
{
Script = script;
if (script.Context is Profile profile)
profile.ChildRemoved += ProfileOnChildRemoved;
LoadLayerProperty();
}
public void LoadLayerProperty()
{
lock (_layerPropertyLock)
{
if (Script.Context is not Profile profile)
return;
if (Storage is not LayerPropertyNodeEntity entity)
return;
RenderProfileElement element = profile.GetAllRenderElements().FirstOrDefault(l => l.EntityId == entity.ElementId);
ProfileElement = element;
LayerProperty = element?.GetAllLayerProperties().FirstOrDefault(p => p.Path == entity.PropertyPath);
CreatePins();
}
}
public void ChangeProfileElement(RenderProfileElement profileElement)
{
lock (_layerPropertyLock)
{
ProfileElement = profileElement;
LayerProperty = null;
Storage = new LayerPropertyNodeEntity
{
ElementId = ProfileElement?.EntityId ?? Guid.Empty,
PropertyPath = null
};
CreatePins();
}
}
public void ChangeLayerProperty(ILayerProperty layerProperty)
{
lock (_layerPropertyLock)
{
LayerProperty = layerProperty;
Storage = new LayerPropertyNodeEntity
{
ElementId = ProfileElement?.EntityId ?? Guid.Empty,
PropertyPath = LayerProperty?.Path
};
CreatePins();
}
}
private void CreatePins()
{
while (Pins.Any())
RemovePin((Pin) Pins.First());
if (LayerProperty == null)
return;
foreach (IDataBindingRegistration dataBindingRegistration in LayerProperty.GetAllDataBindingRegistrations())
CreateOutputPin(dataBindingRegistration.ValueType, dataBindingRegistration.DisplayName);
}
private void ProfileOnChildRemoved(object? sender, EventArgs e)
{
if (Script.Context is not Profile profile)
return;
if (!profile.GetAllRenderElements().Contains(ProfileElement))
ChangeProfileElement(null);
}
}
public class LayerPropertyNodeEntity
{
public Guid ElementId { get; set; }
public string PropertyPath { get; set; }
}
}