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:
parent
836e979991
commit
a4667fdc03
@ -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)
|
||||
|
||||
@ -67,7 +67,7 @@ namespace Artemis.Core
|
||||
DataBinding = new DataBinding<TLayerProperty, TProperty>(LayerProperty, dataBinding);
|
||||
return DataBinding;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ClearDataBinding()
|
||||
{
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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>
|
||||
130
src/Artemis.VisualScripting/Nodes/LayerPropertyNode.cs
Normal file
130
src/Artemis.VisualScripting/Nodes/LayerPropertyNode.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user