mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-31 09:43:46 +00:00
Merge branch 'layer-refactor'
This commit is contained in:
commit
447112729d
@ -1,75 +0,0 @@
|
|||||||
// Pointers used to auto-update.
|
|
||||||
// NOTE: If you're going to copy paste these for your own application, include a link to https://github.com/SpoinkyNL/Artemis
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"Game":"RocketLeague",
|
|
||||||
"GameVersion":"1.30",
|
|
||||||
"GameAddresses":[
|
|
||||||
{
|
|
||||||
"Description":"Boost",
|
|
||||||
"BasePointer":{
|
|
||||||
"value":23986356
|
|
||||||
},
|
|
||||||
"Offsets":[
|
|
||||||
196,
|
|
||||||
24,
|
|
||||||
904,
|
|
||||||
1852,
|
|
||||||
548
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Game":"WorldOfWarcraft",
|
|
||||||
"GameVersion":"7.0.3.22810",
|
|
||||||
"GameAddresses":[
|
|
||||||
{
|
|
||||||
"Description":"ObjectManager",
|
|
||||||
"BasePointer":{
|
|
||||||
"value":22511728
|
|
||||||
},
|
|
||||||
"Offsets":null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Description":"LocalPlayer",
|
|
||||||
"BasePointer":{
|
|
||||||
"value":23715600
|
|
||||||
},
|
|
||||||
"Offsets":null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Description":"NameCache",
|
|
||||||
"BasePointer":{
|
|
||||||
"value":22142184
|
|
||||||
},
|
|
||||||
"Offsets":null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Description":"TargetGuid",
|
|
||||||
"BasePointer":{
|
|
||||||
"value":24758592
|
|
||||||
},
|
|
||||||
"Offsets":null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Game":"Terraria",
|
|
||||||
"GameVersion":"1.3.4.4",
|
|
||||||
"GameAddresses":[
|
|
||||||
{
|
|
||||||
"Description":"PlayerBase",
|
|
||||||
"BasePointer":{
|
|
||||||
"value":3784824
|
|
||||||
},
|
|
||||||
"Offsets":[
|
|
||||||
640,
|
|
||||||
1728,
|
|
||||||
1652,
|
|
||||||
60
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@ -37,6 +37,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Ben.Demystifier" Version="0.1.6" />
|
<PackageReference Include="Ben.Demystifier" Version="0.1.6" />
|
||||||
<PackageReference Include="HidSharp" Version="2.1.0" />
|
<PackageReference Include="HidSharp" Version="2.1.0" />
|
||||||
|
<PackageReference Include="Humanizer.Core" Version="2.8.26" />
|
||||||
<PackageReference Include="LiteDB" Version="5.0.8" />
|
<PackageReference Include="LiteDB" Version="5.0.8" />
|
||||||
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.3.0" />
|
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.3.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=events/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=events/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=events_005Cplugins/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=events_005Cprofiles/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=events_005Cstores/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=exceptions/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=exceptions/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=extensions/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=extensions/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models/@EntryIndexedValue">True</s:Boolean>
|
||||||
@ -20,6 +23,7 @@
|
|||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cprofile_005Clayerproperties_005Ctypes/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cprofile_005Clayerproperties_005Ctypes/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cprofile_005Clayershapes/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cprofile_005Clayershapes/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Csurface/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Csurface/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=placeholders/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Cdatamodelexpansions_005Cattributes/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Cdatamodelexpansions_005Cattributes/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Cdatamodelexpansions_005Cinternal/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Cdatamodelexpansions_005Cinternal/@EntryIndexedValue">True</s:Boolean>
|
||||||
@ -29,6 +33,10 @@
|
|||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Csettings/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Csettings/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=rgb_002Enet/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=rgb_002Enet/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cinterfaces/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cinterfaces/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cregistration/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cregistration_005Cinterfaces/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cstorage/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cstorage/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cstorage_005Cinterfaces/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cstorage_005Cinterfaces/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=stores/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=stores_005Cregistrations/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=utilities/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=utilities/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||||
@ -32,7 +32,13 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The plugin info used by core components of Artemis
|
/// The plugin info used by core components of Artemis
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly PluginInfo CorePluginInfo = new PluginInfo {Guid = Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"), Name = "Artemis Core"};
|
public static readonly PluginInfo CorePluginInfo = new PluginInfo
|
||||||
|
{
|
||||||
|
Guid = Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"), Name = "Artemis Core", Enabled = true
|
||||||
|
};
|
||||||
|
|
||||||
|
internal static readonly CorePlugin CorePlugin = new CorePlugin {PluginInfo = CorePluginInfo};
|
||||||
|
internal static readonly EffectPlaceholderPlugin EffectPlaceholderPlugin = new EffectPlaceholderPlugin {PluginInfo = CorePluginInfo};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A read-only collection containing all primitive numeric types
|
/// A read-only collection containing all primitive numeric types
|
||||||
|
|||||||
@ -1,14 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Artemis.Core
|
|
||||||
{
|
|
||||||
public class LayerPropertyEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
public LayerPropertyEventArgs(BaseLayerProperty layerProperty)
|
|
||||||
{
|
|
||||||
LayerProperty = layerProperty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BaseLayerProperty LayerProperty { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
24
src/Artemis.Core/Events/Profiles/LayerPropertyEventArgs.cs
Normal file
24
src/Artemis.Core/Events/Profiles/LayerPropertyEventArgs.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
public class LayerPropertyEventArgs<T> : EventArgs
|
||||||
|
{
|
||||||
|
public LayerPropertyEventArgs(LayerProperty<T> layerProperty)
|
||||||
|
{
|
||||||
|
LayerProperty = layerProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LayerProperty<T> LayerProperty { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LayerPropertyEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public LayerPropertyEventArgs(ILayerProperty layerProperty)
|
||||||
|
{
|
||||||
|
LayerProperty = layerProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILayerProperty LayerProperty { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
internal class ConditionOperatorStoreEvent
|
||||||
|
{
|
||||||
|
public ConditionOperatorStoreEvent(ConditionOperatorRegistration registration)
|
||||||
|
{
|
||||||
|
Registration = registration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConditionOperatorRegistration Registration { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
internal class DataBindingModifierTypeStoreEvent
|
||||||
|
{
|
||||||
|
public DataBindingModifierTypeStoreEvent(DataBindingModifierTypeRegistration typeRegistration)
|
||||||
|
{
|
||||||
|
TypeRegistration = typeRegistration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataBindingModifierTypeRegistration TypeRegistration { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/Artemis.Core/Events/Stores/DataModelStoreEvent.cs
Normal file
12
src/Artemis.Core/Events/Stores/DataModelStoreEvent.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
internal class DataModelStoreEvent
|
||||||
|
{
|
||||||
|
public DataModelStoreEvent(DataModelRegistration registration)
|
||||||
|
{
|
||||||
|
Registration = registration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataModelRegistration Registration { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/Artemis.Core/Events/Stores/LayerBrushStoreEvent.cs
Normal file
12
src/Artemis.Core/Events/Stores/LayerBrushStoreEvent.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
internal class LayerBrushStoreEvent
|
||||||
|
{
|
||||||
|
public LayerBrushStoreEvent(LayerBrushRegistration registration)
|
||||||
|
{
|
||||||
|
Registration = registration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LayerBrushRegistration Registration { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/Artemis.Core/Events/Stores/LayerEffectStoreEvent.cs
Normal file
12
src/Artemis.Core/Events/Stores/LayerEffectStoreEvent.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
internal class LayerEffectStoreEvent
|
||||||
|
{
|
||||||
|
public LayerEffectStoreEvent(LayerEffectRegistration registration)
|
||||||
|
{
|
||||||
|
Registration = registration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LayerEffectRegistration Registration { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/Artemis.Core/Models/IStorageModel.cs
Normal file
18
src/Artemis.Core/Models/IStorageModel.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a model that can be loaded and saved to persistent storage
|
||||||
|
/// </summary>
|
||||||
|
public interface IStorageModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Loads the model from its associated entity
|
||||||
|
/// </summary>
|
||||||
|
void Load();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Saves the model to its associated entity
|
||||||
|
/// </summary>
|
||||||
|
void Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/Artemis.Core/Models/IUpdateModel.cs
Normal file
14
src/Artemis.Core/Models/IUpdateModel.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a model that updates using a delta time
|
||||||
|
/// </summary>
|
||||||
|
public interface IUpdateModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Performs an update on the model
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="deltaTime">The delta time in seconds</param>
|
||||||
|
void Update(double deltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.Storage.Entities.Profile.Abstract;
|
using Artemis.Storage.Entities.Profile.Abstract;
|
||||||
|
|
||||||
@ -7,7 +8,7 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// An abstract class for display condition parts
|
/// An abstract class for display condition parts
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class DisplayConditionPart
|
public abstract class DisplayConditionPart : IDisposable
|
||||||
{
|
{
|
||||||
private readonly List<DisplayConditionPart> _children = new List<DisplayConditionPart>();
|
private readonly List<DisplayConditionPart> _children = new List<DisplayConditionPart>();
|
||||||
|
|
||||||
@ -60,8 +61,25 @@ namespace Artemis.Core
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public abstract bool EvaluateObject(object target);
|
public abstract bool EvaluateObject(object target);
|
||||||
|
|
||||||
internal abstract void Initialize(IDataModelService dataModelService);
|
internal abstract void Save();
|
||||||
internal abstract void ApplyToEntity();
|
|
||||||
internal abstract DisplayConditionPartEntity GetEntity();
|
internal abstract DisplayConditionPartEntity GetEntity();
|
||||||
|
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2,18 +2,14 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using Artemis.Core.Services;
|
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A display condition operator is used by the conditions system to perform a specific boolean check
|
/// A condition operator is used by the conditions system to perform a specific boolean check
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class DisplayConditionOperator
|
public abstract class ConditionOperator
|
||||||
{
|
{
|
||||||
private IDataModelService _dataModelService;
|
|
||||||
private bool _registered;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the plugin info this condition operator belongs to
|
/// Gets the plugin info this condition operator belongs to
|
||||||
/// <para>Note: Not set until after registering</para>
|
/// <para>Note: Not set until after registering</para>
|
||||||
@ -57,35 +53,5 @@ namespace Artemis.Core
|
|||||||
/// <param name="rightSide">The parameter on the right side of the expression</param>
|
/// <param name="rightSide">The parameter on the right side of the expression</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public abstract BinaryExpression CreateExpression(Expression leftSide, Expression rightSide);
|
public abstract BinaryExpression CreateExpression(Expression leftSide, Expression rightSide);
|
||||||
|
|
||||||
internal void Register(PluginInfo pluginInfo, IDataModelService dataModelService)
|
|
||||||
{
|
|
||||||
if (_registered)
|
|
||||||
return;
|
|
||||||
|
|
||||||
PluginInfo = pluginInfo;
|
|
||||||
_dataModelService = dataModelService;
|
|
||||||
|
|
||||||
if (PluginInfo != Constants.CorePluginInfo)
|
|
||||||
PluginInfo.Instance.PluginDisabled += InstanceOnPluginDisabled;
|
|
||||||
|
|
||||||
_registered = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Unsubscribe()
|
|
||||||
{
|
|
||||||
if (!_registered)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (PluginInfo != Constants.CorePluginInfo)
|
|
||||||
PluginInfo.Instance.PluginDisabled -= InstanceOnPluginDisabled;
|
|
||||||
_registered = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InstanceOnPluginDisabled(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
// The service will call Unsubscribe
|
|
||||||
_dataModelService.RemoveConditionOperator(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Artemis.Core.Services;
|
|
||||||
using Artemis.Storage.Entities.Profile.Abstract;
|
using Artemis.Storage.Entities.Profile.Abstract;
|
||||||
using Artemis.Storage.Entities.Profile.Conditions;
|
using Artemis.Storage.Entities.Profile.Conditions;
|
||||||
|
|
||||||
@ -12,6 +11,8 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class DisplayConditionGroup : DisplayConditionPart
|
public class DisplayConditionGroup : DisplayConditionPart
|
||||||
{
|
{
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the <see cref="DisplayConditionGroup" /> class
|
/// Creates a new instance of the <see cref="DisplayConditionGroup" /> class
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -56,6 +57,9 @@ namespace Artemis.Core
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool Evaluate()
|
public override bool Evaluate()
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("DisplayConditionGroup");
|
||||||
|
|
||||||
// Empty groups are always true
|
// Empty groups are always true
|
||||||
if (Children.Count == 0)
|
if (Children.Count == 0)
|
||||||
return true;
|
return true;
|
||||||
@ -81,6 +85,9 @@ namespace Artemis.Core
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool EvaluateObject(object target)
|
public override bool EvaluateObject(object target)
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("DisplayConditionGroup");
|
||||||
|
|
||||||
// Empty groups are always true
|
// Empty groups are always true
|
||||||
if (Children.Count == 0)
|
if (Children.Count == 0)
|
||||||
return true;
|
return true;
|
||||||
@ -98,26 +105,33 @@ namespace Artemis.Core
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void ApplyToEntity()
|
internal override void Save()
|
||||||
{
|
{
|
||||||
Entity.BooleanOperator = (int) BooleanOperator;
|
Entity.BooleanOperator = (int) BooleanOperator;
|
||||||
|
|
||||||
Entity.Children.Clear();
|
Entity.Children.Clear();
|
||||||
Entity.Children.AddRange(Children.Select(c => c.GetEntity()));
|
Entity.Children.AddRange(Children.Select(c => c.GetEntity()));
|
||||||
foreach (var child in Children)
|
foreach (var child in Children)
|
||||||
child.ApplyToEntity();
|
child.Save();
|
||||||
}
|
|
||||||
|
|
||||||
internal override void Initialize(IDataModelService dataModelService)
|
|
||||||
{
|
|
||||||
foreach (var child in Children)
|
|
||||||
child.Initialize(dataModelService);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override DisplayConditionPartEntity GetEntity()
|
internal override DisplayConditionPartEntity GetEntity()
|
||||||
{
|
{
|
||||||
return Entity;
|
return Entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
_disposed = true;
|
||||||
|
foreach (var child in Children)
|
||||||
|
child.Dispose();
|
||||||
|
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum BooleanOperator
|
public enum BooleanOperator
|
||||||
|
|||||||
@ -3,7 +3,6 @@ using System.Collections;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using Artemis.Core.DataModelExpansions;
|
using Artemis.Core.DataModelExpansions;
|
||||||
using Artemis.Core.Services;
|
|
||||||
using Artemis.Storage.Entities.Profile.Abstract;
|
using Artemis.Storage.Entities.Profile.Abstract;
|
||||||
using Artemis.Storage.Entities.Profile.Conditions;
|
using Artemis.Storage.Entities.Profile.Conditions;
|
||||||
|
|
||||||
@ -11,10 +10,14 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
public class DisplayConditionList : DisplayConditionPart
|
public class DisplayConditionList : DisplayConditionPart
|
||||||
{
|
{
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
public DisplayConditionList(DisplayConditionPart parent)
|
public DisplayConditionList(DisplayConditionPart parent)
|
||||||
{
|
{
|
||||||
Parent = parent;
|
Parent = parent;
|
||||||
Entity = new DisplayConditionListEntity();
|
Entity = new DisplayConditionListEntity();
|
||||||
|
|
||||||
|
Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DisplayConditionList(DisplayConditionPart parent, DisplayConditionListEntity entity)
|
public DisplayConditionList(DisplayConditionPart parent, DisplayConditionListEntity entity)
|
||||||
@ -22,6 +25,8 @@ namespace Artemis.Core
|
|||||||
Parent = parent;
|
Parent = parent;
|
||||||
Entity = entity;
|
Entity = entity;
|
||||||
ListOperator = (ListOperator) entity.ListOperator;
|
ListOperator = (ListOperator) entity.ListOperator;
|
||||||
|
|
||||||
|
Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DisplayConditionListEntity Entity { get; set; }
|
public DisplayConditionListEntity Entity { get; set; }
|
||||||
@ -34,6 +39,9 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
public override bool Evaluate()
|
public override bool Evaluate()
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("DisplayConditionList");
|
||||||
|
|
||||||
if (CompiledListAccessor == null)
|
if (CompiledListAccessor == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -42,6 +50,9 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
public override bool EvaluateObject(object target)
|
public override bool EvaluateObject(object target)
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("DisplayConditionList");
|
||||||
|
|
||||||
if (!Children.Any())
|
if (!Children.Any())
|
||||||
return false;
|
return false;
|
||||||
if (!(target is IList list))
|
if (!(target is IList list))
|
||||||
@ -60,6 +71,9 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
public void UpdateList(DataModel dataModel, string path)
|
public void UpdateList(DataModel dataModel, string path)
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("DisplayConditionList");
|
||||||
|
|
||||||
if (dataModel != null && path == null)
|
if (dataModel != null && path == null)
|
||||||
throw new ArtemisCoreException("If a data model is provided, a path is also required");
|
throw new ArtemisCoreException("If a data model is provided, a path is also required");
|
||||||
if (dataModel == null && path != null)
|
if (dataModel == null && path != null)
|
||||||
@ -90,6 +104,9 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
public void CreateExpression()
|
public void CreateExpression()
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("DisplayConditionList");
|
||||||
|
|
||||||
var parameter = Expression.Parameter(typeof(object), "listDataModel");
|
var parameter = Expression.Parameter(typeof(object), "listDataModel");
|
||||||
var accessor = ListPropertyPath.Split('.').Aggregate<string, Expression>(
|
var accessor = ListPropertyPath.Split('.').Aggregate<string, Expression>(
|
||||||
Expression.Convert(parameter, ListDataModel.GetType()),
|
Expression.Convert(parameter, ListDataModel.GetType()),
|
||||||
@ -98,12 +115,15 @@ namespace Artemis.Core
|
|||||||
var lambda = Expression.Lambda<Func<object, IList>>(accessor, parameter);
|
var lambda = Expression.Lambda<Func<object, IList>>(accessor, parameter);
|
||||||
CompiledListAccessor = lambda.Compile();
|
CompiledListAccessor = lambda.Compile();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void ApplyToEntity()
|
internal override void Save()
|
||||||
{
|
{
|
||||||
// Target list
|
// Target list
|
||||||
Entity.ListDataModelGuid = ListDataModel?.PluginInfo?.Guid;
|
if (ListDataModel != null)
|
||||||
Entity.ListPropertyPath = ListPropertyPath;
|
{
|
||||||
|
Entity.ListDataModelGuid = ListDataModel.PluginInfo.Guid;
|
||||||
|
Entity.ListPropertyPath = ListPropertyPath;
|
||||||
|
}
|
||||||
|
|
||||||
// Operator
|
// Operator
|
||||||
Entity.ListOperator = (int) ListOperator;
|
Entity.ListOperator = (int) ListOperator;
|
||||||
@ -112,7 +132,7 @@ namespace Artemis.Core
|
|||||||
Entity.Children.Clear();
|
Entity.Children.Clear();
|
||||||
Entity.Children.AddRange(Children.Select(c => c.GetEntity()));
|
Entity.Children.AddRange(Children.Select(c => c.GetEntity()));
|
||||||
foreach (var child in Children)
|
foreach (var child in Children)
|
||||||
child.ApplyToEntity();
|
child.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override DisplayConditionPartEntity GetEntity()
|
internal override DisplayConditionPartEntity GetEntity()
|
||||||
@ -120,13 +140,15 @@ namespace Artemis.Core
|
|||||||
return Entity;
|
return Entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Initialize(IDataModelService dataModelService)
|
internal void Initialize()
|
||||||
{
|
{
|
||||||
|
DataModelStore.DataModelAdded += DataModelStoreOnDataModelAdded;
|
||||||
|
DataModelStore.DataModelRemoved += DataModelStoreOnDataModelRemoved;
|
||||||
if (Entity.ListDataModelGuid == null)
|
if (Entity.ListDataModelGuid == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Get the data model and ensure the path is valid
|
// Get the data model and ensure the path is valid
|
||||||
var dataModel = dataModelService.GetPluginDataModelByGuid(Entity.ListDataModelGuid.Value);
|
var dataModel = DataModelStore.Get(Entity.ListDataModelGuid.Value)?.DataModel;
|
||||||
if (dataModel == null || !dataModel.ContainsPath(Entity.ListPropertyPath))
|
if (dataModel == null || !dataModel.ContainsPath(Entity.ListPropertyPath))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -143,9 +165,50 @@ namespace Artemis.Core
|
|||||||
Entity.Children.Clear();
|
Entity.Children.Clear();
|
||||||
AddChild(new DisplayConditionGroup(this));
|
AddChild(new DisplayConditionGroup(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
Children[0].Initialize(dataModelService);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#region Event handlers
|
||||||
|
|
||||||
|
private void DataModelStoreOnDataModelAdded(object sender, DataModelStoreEvent e)
|
||||||
|
{
|
||||||
|
var dataModel = e.Registration.DataModel;
|
||||||
|
if (dataModel.PluginInfo.Guid == Entity.ListDataModelGuid && dataModel.ContainsPath(Entity.ListPropertyPath))
|
||||||
|
{
|
||||||
|
ListDataModel = dataModel;
|
||||||
|
ListPropertyPath = Entity.ListPropertyPath;
|
||||||
|
CreateExpression();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DataModelStoreOnDataModelRemoved(object sender, DataModelStoreEvent e)
|
||||||
|
{
|
||||||
|
if (ListDataModel != e.Registration.DataModel)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ListDataModel = null;
|
||||||
|
CompiledListAccessor = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
_disposed = true;
|
||||||
|
|
||||||
|
DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded;
|
||||||
|
DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved;
|
||||||
|
|
||||||
|
foreach (var child in Children)
|
||||||
|
child.Dispose();
|
||||||
|
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ListOperator
|
public enum ListOperator
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using Artemis.Core.DataModelExpansions;
|
using Artemis.Core.DataModelExpansions;
|
||||||
using Artemis.Core.Services;
|
|
||||||
using Artemis.Storage.Entities.Profile.Abstract;
|
using Artemis.Storage.Entities.Profile.Abstract;
|
||||||
using Artemis.Storage.Entities.Profile.Conditions;
|
using Artemis.Storage.Entities.Profile.Conditions;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@ -18,6 +17,7 @@ namespace Artemis.Core
|
|||||||
Entity = new DisplayConditionListPredicateEntity();
|
Entity = new DisplayConditionListPredicateEntity();
|
||||||
|
|
||||||
ApplyParentList();
|
ApplyParentList();
|
||||||
|
Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DisplayConditionListPredicate(DisplayConditionPart parent, DisplayConditionListPredicateEntity entity)
|
public DisplayConditionListPredicate(DisplayConditionPart parent, DisplayConditionListPredicateEntity entity)
|
||||||
@ -27,12 +27,13 @@ namespace Artemis.Core
|
|||||||
PredicateType = (ProfileRightSideType) entity.PredicateType;
|
PredicateType = (ProfileRightSideType) entity.PredicateType;
|
||||||
|
|
||||||
ApplyParentList();
|
ApplyParentList();
|
||||||
|
Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DisplayConditionListPredicateEntity Entity { get; set; }
|
public DisplayConditionListPredicateEntity Entity { get; set; }
|
||||||
|
|
||||||
public ProfileRightSideType PredicateType { get; set; }
|
public ProfileRightSideType PredicateType { get; set; }
|
||||||
public DisplayConditionOperator Operator { get; private set; }
|
public ConditionOperator Operator { get; private set; }
|
||||||
|
|
||||||
public Type ListType { get; private set; }
|
public Type ListType { get; private set; }
|
||||||
public DataModel ListDataModel { get; private set; }
|
public DataModel ListDataModel { get; private set; }
|
||||||
@ -120,9 +121,9 @@ namespace Artemis.Core
|
|||||||
CreateExpression();
|
CreateExpression();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateOperator(DisplayConditionOperator displayConditionOperator)
|
public void UpdateOperator(ConditionOperator conditionOperator)
|
||||||
{
|
{
|
||||||
if (displayConditionOperator == null)
|
if (conditionOperator == null)
|
||||||
{
|
{
|
||||||
Operator = null;
|
Operator = null;
|
||||||
return;
|
return;
|
||||||
@ -130,13 +131,13 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
if (LeftPropertyPath == null)
|
if (LeftPropertyPath == null)
|
||||||
{
|
{
|
||||||
Operator = displayConditionOperator;
|
Operator = conditionOperator;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var leftType = GetTypeAtInnerPath(LeftPropertyPath);
|
var leftType = GetTypeAtInnerPath(LeftPropertyPath);
|
||||||
if (displayConditionOperator.SupportsType(leftType))
|
if (conditionOperator.SupportsType(leftType))
|
||||||
Operator = displayConditionOperator;
|
Operator = conditionOperator;
|
||||||
|
|
||||||
CreateExpression();
|
CreateExpression();
|
||||||
}
|
}
|
||||||
@ -189,22 +190,31 @@ namespace Artemis.Core
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void ApplyToEntity()
|
internal override void Save()
|
||||||
{
|
{
|
||||||
Entity.PredicateType = (int) PredicateType;
|
Entity.PredicateType = (int) PredicateType;
|
||||||
Entity.ListDataModelGuid = ListDataModel?.PluginInfo?.Guid;
|
if (ListDataModel != null)
|
||||||
Entity.ListPropertyPath = ListPropertyPath;
|
{
|
||||||
|
Entity.ListDataModelGuid = ListDataModel.PluginInfo.Guid;
|
||||||
|
Entity.ListPropertyPath = ListPropertyPath;
|
||||||
|
}
|
||||||
|
|
||||||
Entity.LeftPropertyPath = LeftPropertyPath;
|
Entity.LeftPropertyPath = LeftPropertyPath;
|
||||||
Entity.RightPropertyPath = RightPropertyPath;
|
Entity.RightPropertyPath = RightPropertyPath;
|
||||||
Entity.RightStaticValue = JsonConvert.SerializeObject(RightStaticValue);
|
Entity.RightStaticValue = JsonConvert.SerializeObject(RightStaticValue);
|
||||||
|
|
||||||
Entity.OperatorPluginGuid = Operator?.PluginInfo?.Guid;
|
if (Operator != null)
|
||||||
Entity.OperatorType = Operator?.GetType().Name;
|
{
|
||||||
|
Entity.OperatorPluginGuid = Operator.PluginInfo.Guid;
|
||||||
|
Entity.OperatorType = Operator.GetType().Name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Initialize(IDataModelService dataModelService)
|
private void Initialize()
|
||||||
{
|
{
|
||||||
|
ConditionOperatorStore.ConditionOperatorAdded += ConditionOperatorStoreOnConditionOperatorAdded;
|
||||||
|
ConditionOperatorStore.ConditionOperatorRemoved += ConditionOperatorStoreOnConditionOperatorRemoved;
|
||||||
|
|
||||||
// Left side
|
// Left side
|
||||||
if (Entity.LeftPropertyPath != null && ListContainsInnerPath(Entity.LeftPropertyPath))
|
if (Entity.LeftPropertyPath != null && ListContainsInnerPath(Entity.LeftPropertyPath))
|
||||||
UpdateLeftSide(Entity.LeftPropertyPath);
|
UpdateLeftSide(Entity.LeftPropertyPath);
|
||||||
@ -212,7 +222,7 @@ namespace Artemis.Core
|
|||||||
// Operator
|
// Operator
|
||||||
if (Entity.OperatorPluginGuid != null)
|
if (Entity.OperatorPluginGuid != null)
|
||||||
{
|
{
|
||||||
var conditionOperator = dataModelService.GetConditionOperator(Entity.OperatorPluginGuid.Value, Entity.OperatorType);
|
var conditionOperator = ConditionOperatorStore.Get(Entity.OperatorPluginGuid.Value, Entity.OperatorType)?.ConditionOperator;
|
||||||
if (conditionOperator != null)
|
if (conditionOperator != null)
|
||||||
UpdateOperator(conditionOperator);
|
UpdateOperator(conditionOperator);
|
||||||
}
|
}
|
||||||
@ -221,7 +231,7 @@ namespace Artemis.Core
|
|||||||
if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightPropertyPath != null)
|
if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightPropertyPath != null)
|
||||||
{
|
{
|
||||||
if (ListContainsInnerPath(Entity.RightPropertyPath))
|
if (ListContainsInnerPath(Entity.RightPropertyPath))
|
||||||
UpdateLeftSide(Entity.LeftPropertyPath);
|
UpdateRightSideDynamic(Entity.RightPropertyPath);
|
||||||
}
|
}
|
||||||
// Right side static
|
// Right side static
|
||||||
else if (PredicateType == ProfileRightSideType.Static && Entity.RightStaticValue != null)
|
else if (PredicateType == ProfileRightSideType.Static && Entity.RightStaticValue != null)
|
||||||
@ -241,7 +251,7 @@ namespace Artemis.Core
|
|||||||
// If deserialization fails, use the type's default
|
// If deserialization fails, use the type's default
|
||||||
catch (JsonSerializationException e)
|
catch (JsonSerializationException e)
|
||||||
{
|
{
|
||||||
dataModelService.LogListPredicateDeserializationFailure(this, e);
|
DeserializationLogger.LogListPredicateDeserializationFailure(this, e);
|
||||||
rightSideValue = Activator.CreateInstance(leftSideType);
|
rightSideValue = Activator.CreateInstance(leftSideType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,7 +265,7 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
catch (JsonException e)
|
catch (JsonException e)
|
||||||
{
|
{
|
||||||
dataModelService.LogListPredicateDeserializationFailure(this, e);
|
DeserializationLogger.LogListPredicateDeserializationFailure(this, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -383,5 +393,37 @@ namespace Artemis.Core
|
|||||||
Expression.Property
|
Expression.Property
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Event handlers
|
||||||
|
|
||||||
|
private void ConditionOperatorStoreOnConditionOperatorAdded(object sender, ConditionOperatorStoreEvent e)
|
||||||
|
{
|
||||||
|
var conditionOperator = e.Registration.ConditionOperator;
|
||||||
|
if (Entity.OperatorPluginGuid == conditionOperator.PluginInfo.Guid && Entity.OperatorType == conditionOperator.GetType().Name)
|
||||||
|
UpdateOperator(conditionOperator);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ConditionOperatorStoreOnConditionOperatorRemoved(object sender, ConditionOperatorStoreEvent e)
|
||||||
|
{
|
||||||
|
if (e.Registration.ConditionOperator != Operator)
|
||||||
|
return;
|
||||||
|
Operator = null;
|
||||||
|
CompiledListPredicate = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
ConditionOperatorStore.ConditionOperatorAdded -= ConditionOperatorStoreOnConditionOperatorAdded;
|
||||||
|
ConditionOperatorStore.ConditionOperatorRemoved -= ConditionOperatorStoreOnConditionOperatorRemoved;
|
||||||
|
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2,7 +2,6 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using Artemis.Core.DataModelExpansions;
|
using Artemis.Core.DataModelExpansions;
|
||||||
using Artemis.Core.Services;
|
|
||||||
using Artemis.Storage.Entities.Profile.Abstract;
|
using Artemis.Storage.Entities.Profile.Abstract;
|
||||||
using Artemis.Storage.Entities.Profile.Conditions;
|
using Artemis.Storage.Entities.Profile.Conditions;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@ -25,6 +24,8 @@ namespace Artemis.Core
|
|||||||
Parent = parent;
|
Parent = parent;
|
||||||
PredicateType = predicateType;
|
PredicateType = predicateType;
|
||||||
Entity = new DisplayConditionPredicateEntity();
|
Entity = new DisplayConditionPredicateEntity();
|
||||||
|
|
||||||
|
Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -37,6 +38,8 @@ namespace Artemis.Core
|
|||||||
Parent = parent;
|
Parent = parent;
|
||||||
Entity = entity;
|
Entity = entity;
|
||||||
PredicateType = (ProfileRightSideType) entity.PredicateType;
|
PredicateType = (ProfileRightSideType) entity.PredicateType;
|
||||||
|
|
||||||
|
Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -47,7 +50,7 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the operator
|
/// Gets the operator
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DisplayConditionOperator Operator { get; private set; }
|
public ConditionOperator Operator { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the currently used instance of the left data model
|
/// Gets the currently used instance of the left data model
|
||||||
@ -175,11 +178,11 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the operator of the predicate and re-compiles the expression
|
/// Updates the operator of the predicate and re-compiles the expression
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="displayConditionOperator"></param>
|
/// <param name="conditionOperator"></param>
|
||||||
public void UpdateOperator(DisplayConditionOperator displayConditionOperator)
|
public void UpdateOperator(ConditionOperator conditionOperator)
|
||||||
{
|
{
|
||||||
// Calling CreateExpression will clear compiled expressions
|
// Calling CreateExpression will clear compiled expressions
|
||||||
if (displayConditionOperator == null)
|
if (conditionOperator == null)
|
||||||
{
|
{
|
||||||
Operator = null;
|
Operator = null;
|
||||||
CreateExpression();
|
CreateExpression();
|
||||||
@ -189,18 +192,18 @@ namespace Artemis.Core
|
|||||||
// No need to clear compiled expressions, without a left data model they are already null
|
// No need to clear compiled expressions, without a left data model they are already null
|
||||||
if (LeftDataModel == null)
|
if (LeftDataModel == null)
|
||||||
{
|
{
|
||||||
Operator = displayConditionOperator;
|
Operator = conditionOperator;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var leftType = LeftDataModel.GetTypeAtPath(LeftPropertyPath);
|
var leftType = LeftDataModel.GetTypeAtPath(LeftPropertyPath);
|
||||||
if (!displayConditionOperator.SupportsType(leftType))
|
if (!conditionOperator.SupportsType(leftType))
|
||||||
{
|
{
|
||||||
throw new ArtemisCoreException($"Cannot apply operator {displayConditionOperator.GetType().Name} to this predicate because " +
|
throw new ArtemisCoreException($"Cannot apply operator {conditionOperator.GetType().Name} to this predicate because " +
|
||||||
$"it does not support left side type {leftType.Name}");
|
$"it does not support left side type {leftType.Name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
Operator = displayConditionOperator;
|
Operator = conditionOperator;
|
||||||
CreateExpression();
|
CreateExpression();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +232,7 @@ namespace Artemis.Core
|
|||||||
return $"[Static] {LeftPropertyPath} {Operator.Description} {RightStaticValue}";
|
return $"[Static] {LeftPropertyPath} {Operator.Description} {RightStaticValue}";
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void ApplyToEntity()
|
internal override void Save()
|
||||||
{
|
{
|
||||||
Entity.PredicateType = (int) PredicateType;
|
Entity.PredicateType = (int) PredicateType;
|
||||||
Entity.LeftDataModelGuid = LeftDataModel?.PluginInfo?.Guid;
|
Entity.LeftDataModelGuid = LeftDataModel?.PluginInfo?.Guid;
|
||||||
@ -243,12 +246,17 @@ namespace Artemis.Core
|
|||||||
Entity.OperatorType = Operator?.GetType().Name;
|
Entity.OperatorType = Operator?.GetType().Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Initialize(IDataModelService dataModelService)
|
internal void Initialize()
|
||||||
{
|
{
|
||||||
|
DataModelStore.DataModelAdded += DataModelStoreOnDataModelAdded;
|
||||||
|
DataModelStore.DataModelRemoved += DataModelStoreOnDataModelRemoved;
|
||||||
|
ConditionOperatorStore.ConditionOperatorAdded += ConditionOperatorStoreOnConditionOperatorAdded;
|
||||||
|
ConditionOperatorStore.ConditionOperatorRemoved += ConditionOperatorStoreOnConditionOperatorRemoved;
|
||||||
|
|
||||||
// Left side
|
// Left side
|
||||||
if (Entity.LeftDataModelGuid != null)
|
if (Entity.LeftDataModelGuid != null)
|
||||||
{
|
{
|
||||||
var dataModel = dataModelService.GetPluginDataModelByGuid(Entity.LeftDataModelGuid.Value);
|
var dataModel = DataModelStore.Get(Entity.LeftDataModelGuid.Value)?.DataModel;
|
||||||
if (dataModel != null && dataModel.ContainsPath(Entity.LeftPropertyPath))
|
if (dataModel != null && dataModel.ContainsPath(Entity.LeftPropertyPath))
|
||||||
UpdateLeftSide(dataModel, Entity.LeftPropertyPath);
|
UpdateLeftSide(dataModel, Entity.LeftPropertyPath);
|
||||||
}
|
}
|
||||||
@ -256,7 +264,7 @@ namespace Artemis.Core
|
|||||||
// Operator
|
// Operator
|
||||||
if (Entity.OperatorPluginGuid != null)
|
if (Entity.OperatorPluginGuid != null)
|
||||||
{
|
{
|
||||||
var conditionOperator = dataModelService.GetConditionOperator(Entity.OperatorPluginGuid.Value, Entity.OperatorType);
|
var conditionOperator = ConditionOperatorStore.Get(Entity.OperatorPluginGuid.Value, Entity.OperatorType)?.ConditionOperator;
|
||||||
if (conditionOperator != null)
|
if (conditionOperator != null)
|
||||||
UpdateOperator(conditionOperator);
|
UpdateOperator(conditionOperator);
|
||||||
}
|
}
|
||||||
@ -264,7 +272,7 @@ namespace Artemis.Core
|
|||||||
// Right side dynamic
|
// Right side dynamic
|
||||||
if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightDataModelGuid != null)
|
if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightDataModelGuid != null)
|
||||||
{
|
{
|
||||||
var dataModel = dataModelService.GetPluginDataModelByGuid(Entity.RightDataModelGuid.Value);
|
var dataModel = DataModelStore.Get(Entity.RightDataModelGuid.Value)?.DataModel;
|
||||||
if (dataModel != null && dataModel.ContainsPath(Entity.RightPropertyPath))
|
if (dataModel != null && dataModel.ContainsPath(Entity.RightPropertyPath))
|
||||||
UpdateRightSide(dataModel, Entity.RightPropertyPath);
|
UpdateRightSide(dataModel, Entity.RightPropertyPath);
|
||||||
}
|
}
|
||||||
@ -286,7 +294,7 @@ namespace Artemis.Core
|
|||||||
// If deserialization fails, use the type's default
|
// If deserialization fails, use the type's default
|
||||||
catch (JsonSerializationException e)
|
catch (JsonSerializationException e)
|
||||||
{
|
{
|
||||||
dataModelService.LogPredicateDeserializationFailure(this, e);
|
DeserializationLogger.LogPredicateDeserializationFailure(this, e);
|
||||||
rightSideValue = Activator.CreateInstance(leftSideType);
|
rightSideValue = Activator.CreateInstance(leftSideType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -409,17 +417,60 @@ namespace Artemis.Core
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Expression CreateListAccessor(DataModel dataModel, string path, ParameterExpression listParameter)
|
#region Event handlers
|
||||||
{
|
|
||||||
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);
|
private void DataModelStoreOnDataModelAdded(object sender, DataModelStoreEvent e)
|
||||||
return path.Split('.').Aggregate<string, Expression>(
|
{
|
||||||
Expression.Convert(listParameter, listType), // Cast to the appropriate type
|
var dataModel = e.Registration.DataModel;
|
||||||
Expression.Property
|
if (dataModel.PluginInfo.Guid == Entity.LeftDataModelGuid && dataModel.ContainsPath(Entity.LeftPropertyPath))
|
||||||
);
|
UpdateLeftSide(dataModel, Entity.LeftPropertyPath);
|
||||||
|
if (dataModel.PluginInfo.Guid == Entity.RightDataModelGuid && dataModel.ContainsPath(Entity.RightPropertyPath))
|
||||||
|
UpdateRightSide(dataModel, Entity.RightPropertyPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DataModelStoreOnDataModelRemoved(object sender, DataModelStoreEvent e)
|
||||||
|
{
|
||||||
|
if (LeftDataModel == e.Registration.DataModel)
|
||||||
|
{
|
||||||
|
CompiledDynamicPredicate = null;
|
||||||
|
LeftDataModel = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RightDataModel == e.Registration.DataModel)
|
||||||
|
{
|
||||||
|
CompiledDynamicPredicate = null;
|
||||||
|
RightDataModel = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 />
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded;
|
||||||
|
DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved;
|
||||||
|
ConditionOperatorStore.ConditionOperatorAdded -= ConditionOperatorStoreOnConditionOperatorAdded;
|
||||||
|
ConditionOperatorStore.ConditionOperatorRemoved -= ConditionOperatorStoreOnConditionOperatorRemoved;
|
||||||
|
|
||||||
|
base.Dispose(disposing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4,7 +4,7 @@ using System.Linq.Expressions;
|
|||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
internal class EqualsConditionOperator : DisplayConditionOperator
|
internal class EqualsConditionOperator : ConditionOperator
|
||||||
{
|
{
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(object)};
|
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(object)};
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ using System.Linq.Expressions;
|
|||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
internal class GreaterThanConditionOperator : DisplayConditionOperator
|
internal class GreaterThanConditionOperator : ConditionOperator
|
||||||
{
|
{
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
|
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ using System.Linq.Expressions;
|
|||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
internal class GreaterThanOrEqualConditionOperator : DisplayConditionOperator
|
internal class GreaterThanOrEqualConditionOperator : ConditionOperator
|
||||||
{
|
{
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
|
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ using System.Linq.Expressions;
|
|||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
internal class LessThanConditionOperator : DisplayConditionOperator
|
internal class LessThanConditionOperator : ConditionOperator
|
||||||
{
|
{
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
|
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ using System.Linq.Expressions;
|
|||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
internal class LessThanOrEqualConditionOperator : DisplayConditionOperator
|
internal class LessThanOrEqualConditionOperator : ConditionOperator
|
||||||
{
|
{
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
|
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ using System.Linq.Expressions;
|
|||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
internal class NotEqualConditionOperator : DisplayConditionOperator
|
internal class NotEqualConditionOperator : ConditionOperator
|
||||||
{
|
{
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(object)};
|
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(object)};
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ using System.Reflection;
|
|||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
internal class StringContainsConditionOperator : DisplayConditionOperator
|
internal class StringContainsConditionOperator : ConditionOperator
|
||||||
{
|
{
|
||||||
private readonly MethodInfo _contains;
|
private readonly MethodInfo _contains;
|
||||||
private readonly MethodInfo _toLower;
|
private readonly MethodInfo _toLower;
|
||||||
|
|||||||
@ -5,7 +5,7 @@ using System.Reflection;
|
|||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
internal class StringEndsWithConditionOperator : DisplayConditionOperator
|
internal class StringEndsWithConditionOperator : ConditionOperator
|
||||||
{
|
{
|
||||||
private readonly MethodInfo _endsWith;
|
private readonly MethodInfo _endsWith;
|
||||||
private readonly MethodInfo _toLower;
|
private readonly MethodInfo _toLower;
|
||||||
|
|||||||
@ -5,7 +5,7 @@ using System.Reflection;
|
|||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
internal class StringEqualsConditionOperator : DisplayConditionOperator
|
internal class StringEqualsConditionOperator : ConditionOperator
|
||||||
{
|
{
|
||||||
private readonly MethodInfo _toLower;
|
private readonly MethodInfo _toLower;
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ using System.Reflection;
|
|||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
internal class StringNotContainsConditionOperator : DisplayConditionOperator
|
internal class StringNotContainsConditionOperator : ConditionOperator
|
||||||
{
|
{
|
||||||
private readonly MethodInfo _contains;
|
private readonly MethodInfo _contains;
|
||||||
private readonly MethodInfo _toLower;
|
private readonly MethodInfo _toLower;
|
||||||
|
|||||||
@ -5,7 +5,7 @@ using System.Reflection;
|
|||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
internal class StringNotEqualConditionOperator : DisplayConditionOperator
|
internal class StringNotEqualConditionOperator : ConditionOperator
|
||||||
{
|
{
|
||||||
private readonly MethodInfo _toLower;
|
private readonly MethodInfo _toLower;
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ using System.Linq.Expressions;
|
|||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
internal class StringNullConditionOperator : DisplayConditionOperator
|
internal class StringNullConditionOperator : ConditionOperator
|
||||||
{
|
{
|
||||||
public StringNullConditionOperator()
|
public StringNullConditionOperator()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -5,7 +5,7 @@ using System.Reflection;
|
|||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
internal class StringStartsWithConditionOperator : DisplayConditionOperator
|
internal class StringStartsWithConditionOperator : ConditionOperator
|
||||||
{
|
{
|
||||||
private readonly MethodInfo _startsWith;
|
private readonly MethodInfo _startsWith;
|
||||||
private readonly MethodInfo _toLower;
|
private readonly MethodInfo _toLower;
|
||||||
|
|||||||
@ -1,48 +1,51 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public class FloatDataBindingConverter : DataBindingConverter
|
public class FloatDataBindingConverter : FloatDataBindingConverter<float>
|
||||||
{
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
/// <typeparam name="T">The type of layer property this converter is applied to</typeparam>
|
||||||
|
public class FloatDataBindingConverter<T> : DataBindingConverter<T, float>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="FloatDataBindingConverter{T}" /> class
|
||||||
|
/// </summary>
|
||||||
public FloatDataBindingConverter()
|
public FloatDataBindingConverter()
|
||||||
{
|
{
|
||||||
SupportedType = typeof(float);
|
|
||||||
SupportsSum = true;
|
SupportsSum = true;
|
||||||
SupportsInterpolate = true;
|
SupportsInterpolate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override object Sum(object a, object b)
|
public override float Sum(float a, float b)
|
||||||
{
|
{
|
||||||
return Convert.ToSingle(a) + Convert.ToSingle(b);
|
return a + b;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override object Interpolate(object a, object b, double progress)
|
public override float Interpolate(float a, float b, double progress)
|
||||||
{
|
{
|
||||||
var floatA = Convert.ToSingle(a);
|
var diff = b - a;
|
||||||
var floatB = Convert.ToSingle(b);
|
return (float) (a + diff * progress);
|
||||||
var diff = floatB - floatA;
|
|
||||||
return floatA + diff * progress;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void ApplyValue(object value)
|
public override void ApplyValue(float value)
|
||||||
{
|
{
|
||||||
var floatValue = Convert.ToSingle(value);
|
if (ValueTypeSetExpression == null)
|
||||||
|
return;
|
||||||
|
|
||||||
if (DataBinding.LayerProperty.PropertyDescription.MaxInputValue is float max)
|
if (DataBinding.LayerProperty.PropertyDescription.MaxInputValue is float max)
|
||||||
floatValue = Math.Min(floatValue, max);
|
value = Math.Min(value, max);
|
||||||
if (DataBinding.LayerProperty.PropertyDescription.MinInputValue is float min)
|
if (DataBinding.LayerProperty.PropertyDescription.MinInputValue is float min)
|
||||||
floatValue = Math.Max(floatValue, min);
|
value = Math.Max(value, min);
|
||||||
|
|
||||||
ValueSetter?.Invoke(floatValue);
|
base.ApplyValue(value);
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override object GetValue()
|
|
||||||
{
|
|
||||||
return ValueGetter?.Invoke();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3,11 +3,10 @@
|
|||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public class GeneralDataBindingConverter : DataBindingConverter
|
public class GeneralDataBindingConverter<T> : DataBindingConverter<T, object> where T : ILayerProperty
|
||||||
{
|
{
|
||||||
public GeneralDataBindingConverter()
|
public GeneralDataBindingConverter()
|
||||||
{
|
{
|
||||||
SupportedType = typeof(object);
|
|
||||||
SupportsSum = false;
|
SupportsSum = false;
|
||||||
SupportsInterpolate = false;
|
SupportsInterpolate = false;
|
||||||
}
|
}
|
||||||
@ -23,17 +22,5 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void ApplyValue(object value)
|
|
||||||
{
|
|
||||||
ValueSetter?.Invoke(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override object GetValue()
|
|
||||||
{
|
|
||||||
return ValueGetter?.Invoke();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3,11 +3,18 @@
|
|||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public class IntDataBindingConverter : DataBindingConverter
|
public class IntDataBindingConverter : IntDataBindingConverter<int>
|
||||||
{
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public class IntDataBindingConverter<T> : DataBindingConverter<T, int>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="IntDataBindingConverter{T}" /> class
|
||||||
|
/// </summary>
|
||||||
public IntDataBindingConverter()
|
public IntDataBindingConverter()
|
||||||
{
|
{
|
||||||
SupportedType = typeof(int);
|
|
||||||
SupportsSum = true;
|
SupportsSum = true;
|
||||||
SupportsInterpolate = true;
|
SupportsInterpolate = true;
|
||||||
}
|
}
|
||||||
@ -19,30 +26,16 @@ namespace Artemis.Core
|
|||||||
public MidpointRounding InterpolationRoundingMode { get; set; } = MidpointRounding.AwayFromZero;
|
public MidpointRounding InterpolationRoundingMode { get; set; } = MidpointRounding.AwayFromZero;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override object Sum(object a, object b)
|
public override int Sum(int a, int b)
|
||||||
{
|
{
|
||||||
return (int) a + (int) b;
|
return a + b;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override object Interpolate(object a, object b, double progress)
|
public override int Interpolate(int a, int b, double progress)
|
||||||
{
|
{
|
||||||
var intA = (int) a;
|
var diff = b - a;
|
||||||
var intB = (int) b;
|
return (int) Math.Round(a + diff * progress, InterpolationRoundingMode);
|
||||||
var diff = intB - intA;
|
|
||||||
return (int) Math.Round(intA + diff * progress, InterpolationRoundingMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void ApplyValue(object value)
|
|
||||||
{
|
|
||||||
ValueSetter?.Invoke(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override object GetValue()
|
|
||||||
{
|
|
||||||
return ValueGetter?.Invoke();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
using System;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
// This is internal because it's mainly a proof-of-concept
|
||||||
|
internal class SKColorArgbDataBindingConverter : DataBindingConverter<SKColor, byte>
|
||||||
|
{
|
||||||
|
private readonly Channel _channel;
|
||||||
|
|
||||||
|
public SKColorArgbDataBindingConverter(Channel channel)
|
||||||
|
{
|
||||||
|
_channel = channel;
|
||||||
|
|
||||||
|
SupportsSum = true;
|
||||||
|
SupportsInterpolate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte Sum(byte a, byte b)
|
||||||
|
{
|
||||||
|
return ClampToByte(a + b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte Interpolate(byte a, byte b, double progress)
|
||||||
|
{
|
||||||
|
var diff = b - a;
|
||||||
|
return ClampToByte(a + diff * progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ApplyValue(byte value)
|
||||||
|
{
|
||||||
|
switch (_channel)
|
||||||
|
{
|
||||||
|
case Channel.Alpha:
|
||||||
|
DataBinding.LayerProperty.CurrentValue = DataBinding.LayerProperty.CurrentValue.WithAlpha(value);
|
||||||
|
break;
|
||||||
|
case Channel.Red:
|
||||||
|
DataBinding.LayerProperty.CurrentValue = DataBinding.LayerProperty.CurrentValue.WithRed(value);
|
||||||
|
break;
|
||||||
|
case Channel.Green:
|
||||||
|
DataBinding.LayerProperty.CurrentValue = DataBinding.LayerProperty.CurrentValue.WithGreen(value);
|
||||||
|
break;
|
||||||
|
case Channel.Blue:
|
||||||
|
DataBinding.LayerProperty.CurrentValue = DataBinding.LayerProperty.CurrentValue.WithBlue(value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte ConvertFromObject(object source)
|
||||||
|
{
|
||||||
|
var saveValue = Convert.ToDouble(source);
|
||||||
|
return ClampToByte(saveValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte ClampToByte(double value)
|
||||||
|
{
|
||||||
|
return (byte) Math.Clamp(value, 0, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Channel
|
||||||
|
{
|
||||||
|
Alpha,
|
||||||
|
Red,
|
||||||
|
Green,
|
||||||
|
Blue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,95 +0,0 @@
|
|||||||
using System;
|
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.Core
|
|
||||||
{
|
|
||||||
// This is internal because it's mainly a proof-of-concept
|
|
||||||
internal class SKColorPartDataBindingConverter : DataBindingConverter
|
|
||||||
{
|
|
||||||
private readonly Channel _channel;
|
|
||||||
|
|
||||||
public SKColorPartDataBindingConverter(Channel channel)
|
|
||||||
{
|
|
||||||
_channel = channel;
|
|
||||||
|
|
||||||
SupportsSum = true;
|
|
||||||
SupportsInterpolate = true;
|
|
||||||
SupportedType = _channel switch
|
|
||||||
{
|
|
||||||
Channel.Alpha => typeof(byte),
|
|
||||||
Channel.Red => typeof(byte),
|
|
||||||
Channel.Green => typeof(byte),
|
|
||||||
Channel.Blue => typeof(byte),
|
|
||||||
Channel.Hue => typeof(float),
|
|
||||||
_ => throw new ArgumentOutOfRangeException()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public override object Sum(object a, object b)
|
|
||||||
{
|
|
||||||
return (float) a + (float) b;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override object Interpolate(object a, object b, double progress)
|
|
||||||
{
|
|
||||||
var diff = (float) b - (float) a;
|
|
||||||
return diff * progress;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ApplyValue(object value)
|
|
||||||
{
|
|
||||||
var property = (SKColorLayerProperty) DataBinding.LayerProperty;
|
|
||||||
switch (_channel)
|
|
||||||
{
|
|
||||||
case Channel.Alpha:
|
|
||||||
property.CurrentValue = property.CurrentValue.WithAlpha((byte) value);
|
|
||||||
break;
|
|
||||||
case Channel.Red:
|
|
||||||
property.CurrentValue = property.CurrentValue.WithRed((byte) value);
|
|
||||||
break;
|
|
||||||
case Channel.Green:
|
|
||||||
property.CurrentValue = property.CurrentValue.WithGreen((byte) value);
|
|
||||||
break;
|
|
||||||
case Channel.Blue:
|
|
||||||
property.CurrentValue = property.CurrentValue.WithBlue((byte) value);
|
|
||||||
break;
|
|
||||||
case Channel.Hue:
|
|
||||||
property.CurrentValue.ToHsv(out var h, out var s, out var v);
|
|
||||||
property.CurrentValue = SKColor.FromHsv((float) value, s, v);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override object GetValue()
|
|
||||||
{
|
|
||||||
var property = (SKColorLayerProperty) DataBinding.LayerProperty;
|
|
||||||
switch (_channel)
|
|
||||||
{
|
|
||||||
case Channel.Alpha:
|
|
||||||
return property.CurrentValue.Alpha;
|
|
||||||
case Channel.Red:
|
|
||||||
return property.CurrentValue.Red;
|
|
||||||
case Channel.Green:
|
|
||||||
return property.CurrentValue.Green;
|
|
||||||
case Channel.Blue:
|
|
||||||
return property.CurrentValue.Blue;
|
|
||||||
case Channel.Hue:
|
|
||||||
property.CurrentValue.ToHsv(out var h, out _, out _);
|
|
||||||
return h;
|
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Channel
|
|
||||||
{
|
|
||||||
Alpha,
|
|
||||||
Red,
|
|
||||||
Green,
|
|
||||||
Blue,
|
|
||||||
Hue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -2,60 +2,55 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Reflection;
|
|
||||||
using Artemis.Core.DataModelExpansions;
|
using Artemis.Core.DataModelExpansions;
|
||||||
using Artemis.Core.Services;
|
|
||||||
using Artemis.Storage.Entities.Profile.DataBindings;
|
using Artemis.Storage.Entities.Profile.DataBindings;
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// A data binding that binds a certain <see cref="BaseLayerProperty" /> to a value inside a <see cref="DataModel" />
|
public class DataBinding<TLayerProperty, TProperty> : IDataBinding
|
||||||
/// </summary>
|
|
||||||
public class DataBinding
|
|
||||||
{
|
{
|
||||||
private readonly List<DataBindingModifier> _modifiers = new List<DataBindingModifier>();
|
private readonly List<DataBindingModifier<TLayerProperty, TProperty>> _modifiers = new List<DataBindingModifier<TLayerProperty, TProperty>>();
|
||||||
|
|
||||||
private object _currentValue;
|
private TProperty _currentValue;
|
||||||
private object _previousValue;
|
private bool _disposed;
|
||||||
private TimeSpan _easingProgress;
|
private TimeSpan _easingProgress;
|
||||||
|
private TProperty _previousValue;
|
||||||
|
|
||||||
internal DataBinding(DataBindingRegistration dataBindingRegistration)
|
internal DataBinding(DataBindingRegistration<TLayerProperty, TProperty> dataBindingRegistration)
|
||||||
{
|
{
|
||||||
LayerProperty = dataBindingRegistration.LayerProperty;
|
LayerProperty = dataBindingRegistration.LayerProperty;
|
||||||
Entity = new DataBindingEntity();
|
Entity = new DataBindingEntity();
|
||||||
|
|
||||||
ApplyRegistration(dataBindingRegistration);
|
ApplyRegistration(dataBindingRegistration);
|
||||||
ApplyToEntity();
|
Save();
|
||||||
|
Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal DataBinding(BaseLayerProperty layerProperty, DataBindingEntity entity)
|
internal DataBinding(LayerProperty<TLayerProperty> layerProperty, DataBindingEntity entity)
|
||||||
{
|
{
|
||||||
LayerProperty = layerProperty;
|
LayerProperty = layerProperty;
|
||||||
Entity = entity;
|
Entity = entity;
|
||||||
|
|
||||||
ApplyToDataBinding();
|
// Load will add children so be initialized before that
|
||||||
|
Initialize();
|
||||||
|
Load();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the layer property this data binding targets
|
|
||||||
/// </summary>
|
|
||||||
public BaseLayerProperty LayerProperty { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the data binding registration this data binding is based upon
|
/// Gets the data binding registration this data binding is based upon
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DataBindingRegistration Registration { get; private set; }
|
public DataBindingRegistration<TLayerProperty, TProperty> Registration { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the layer property this data binding targets
|
||||||
|
/// </summary>
|
||||||
|
public LayerProperty<TLayerProperty> LayerProperty { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the converter used to apply this data binding to the <see cref="LayerProperty" />
|
/// Gets the converter used to apply this data binding to the <see cref="LayerProperty" />
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DataBindingConverter Converter { get; private set; }
|
public DataBindingConverter<TLayerProperty, TProperty> Converter { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the property on the <see cref="LayerProperty" /> this data binding targets
|
|
||||||
/// </summary>
|
|
||||||
public PropertyInfo TargetProperty { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the currently used instance of the data model that contains the source of the data binding
|
/// Gets the currently used instance of the data model that contains the source of the data binding
|
||||||
@ -82,7 +77,7 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a list of modifiers applied to this data binding
|
/// Gets a list of modifiers applied to this data binding
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IReadOnlyList<DataBindingModifier> Modifiers => _modifiers.AsReadOnly();
|
public IReadOnlyList<DataBindingModifier<TLayerProperty, TProperty>> Modifiers => _modifiers.AsReadOnly();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the compiled function that gets the current value of the data binding target
|
/// Gets the compiled function that gets the current value of the data binding target
|
||||||
@ -91,11 +86,57 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
internal DataBindingEntity Entity { get; }
|
internal DataBindingEntity Entity { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the smoothing progress of the data binding
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="deltaTime">The time in seconds that passed since the last update</param>
|
||||||
|
public void Update(double deltaTime)
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("DataBinding");
|
||||||
|
|
||||||
|
// Data bindings cannot go back in time like brushes
|
||||||
|
deltaTime = Math.Max(0, deltaTime);
|
||||||
|
|
||||||
|
_easingProgress = _easingProgress.Add(TimeSpan.FromSeconds(deltaTime));
|
||||||
|
if (_easingProgress > EasingTime)
|
||||||
|
_easingProgress = EasingTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Apply()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("DataBinding");
|
||||||
|
|
||||||
|
if (Converter == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var converterValue = Converter.GetValue();
|
||||||
|
var value = GetValue(converterValue);
|
||||||
|
Converter.ApplyValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_disposed = true;
|
||||||
|
|
||||||
|
DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded;
|
||||||
|
DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved;
|
||||||
|
|
||||||
|
foreach (var dataBindingModifier in Modifiers)
|
||||||
|
dataBindingModifier.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a modifier to the data binding's <see cref="Modifiers" /> collection
|
/// Adds a modifier to the data binding's <see cref="Modifiers" /> collection
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void AddModifier(DataBindingModifier modifier)
|
public void AddModifier(DataBindingModifier<TLayerProperty, TProperty> modifier)
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("DataBinding");
|
||||||
|
|
||||||
if (!_modifiers.Contains(modifier))
|
if (!_modifiers.Contains(modifier))
|
||||||
{
|
{
|
||||||
modifier.DataBinding = this;
|
modifier.DataBinding = this;
|
||||||
@ -108,8 +149,11 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes a modifier from the data binding's <see cref="Modifiers" /> collection
|
/// Removes a modifier from the data binding's <see cref="Modifiers" /> collection
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void RemoveModifier(DataBindingModifier modifier)
|
public void RemoveModifier(DataBindingModifier<TLayerProperty, TProperty> modifier)
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("DataBinding");
|
||||||
|
|
||||||
if (_modifiers.Contains(modifier))
|
if (_modifiers.Contains(modifier))
|
||||||
{
|
{
|
||||||
modifier.DataBinding = null;
|
modifier.DataBinding = null;
|
||||||
@ -126,6 +170,9 @@ namespace Artemis.Core
|
|||||||
/// <param name="path">The path pointing to the source inside the data model</param>
|
/// <param name="path">The path pointing to the source inside the data model</param>
|
||||||
public void UpdateSource(DataModel dataModel, string path)
|
public void UpdateSource(DataModel dataModel, string path)
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("DataBinding");
|
||||||
|
|
||||||
if (dataModel != null && path == null)
|
if (dataModel != null && path == null)
|
||||||
throw new ArtemisCoreException("If a data model is provided, a path is also required");
|
throw new ArtemisCoreException("If a data model is provided, a path is also required");
|
||||||
if (dataModel == null && path != null)
|
if (dataModel == null && path != null)
|
||||||
@ -147,8 +194,11 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="baseValue">The base value of the property the data binding is applied to</param>
|
/// <param name="baseValue">The base value of the property the data binding is applied to</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public object GetValue(object baseValue)
|
public TProperty GetValue(TProperty baseValue)
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("DataBinding");
|
||||||
|
|
||||||
if (CompiledTargetAccessor == null || Converter == null)
|
if (CompiledTargetAccessor == null || Converter == null)
|
||||||
return baseValue;
|
return baseValue;
|
||||||
|
|
||||||
@ -156,8 +206,8 @@ namespace Artemis.Core
|
|||||||
foreach (var dataBindingModifier in Modifiers)
|
foreach (var dataBindingModifier in Modifiers)
|
||||||
dataBindingValue = dataBindingModifier.Apply(dataBindingValue);
|
dataBindingValue = dataBindingModifier.Apply(dataBindingValue);
|
||||||
|
|
||||||
var value = Convert.ChangeType(dataBindingValue, TargetProperty.PropertyType);
|
TProperty value = Converter.ConvertFromObject(dataBindingValue);
|
||||||
|
|
||||||
// If no easing is to be applied simple return whatever the current value is
|
// If no easing is to be applied simple return whatever the current value is
|
||||||
if (EasingTime == TimeSpan.Zero || !Converter.SupportsInterpolate)
|
if (EasingTime == TimeSpan.Zero || !Converter.SupportsInterpolate)
|
||||||
return value;
|
return value;
|
||||||
@ -170,19 +220,12 @@ namespace Artemis.Core
|
|||||||
return GetInterpolatedValue();
|
return GetInterpolatedValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ResetEasing(object value)
|
|
||||||
{
|
|
||||||
_previousValue = GetInterpolatedValue();
|
|
||||||
_currentValue = value;
|
|
||||||
_easingProgress = TimeSpan.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the type of the target property of this data binding
|
/// Returns the type of the target property of this data binding
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Type GetTargetType()
|
public Type GetTargetType()
|
||||||
{
|
{
|
||||||
return TargetProperty.PropertyType;
|
return Registration.PropertyExpression.ReturnType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -193,105 +236,47 @@ namespace Artemis.Core
|
|||||||
return SourceDataModel?.GetTypeAtPath(SourcePropertyPath);
|
return SourceDataModel?.GetTypeAtPath(SourcePropertyPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private void Initialize()
|
||||||
/// Updates the smoothing progress of the data binding
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="deltaTime">The time in seconds that passed since the last update</param>
|
|
||||||
public void Update(double deltaTime)
|
|
||||||
{
|
{
|
||||||
// Data bindings cannot go back in time like brushes
|
DataModelStore.DataModelAdded += DataModelStoreOnDataModelAdded;
|
||||||
deltaTime = Math.Max(0, deltaTime);
|
DataModelStore.DataModelRemoved += DataModelStoreOnDataModelRemoved;
|
||||||
|
|
||||||
_easingProgress = _easingProgress.Add(TimeSpan.FromSeconds(deltaTime));
|
|
||||||
if (_easingProgress > EasingTime)
|
|
||||||
_easingProgress = EasingTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Updates the value on the <see cref="LayerProperty" /> according to the data binding
|
|
||||||
/// </summary>
|
|
||||||
public void ApplyToProperty()
|
|
||||||
{
|
|
||||||
if (Converter == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var value = GetValue(Converter.GetValue());
|
|
||||||
Converter.ApplyValue(GetValue(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ApplyToEntity()
|
|
||||||
{
|
|
||||||
// General
|
|
||||||
Entity.TargetProperty = TargetProperty?.Name;
|
|
||||||
Entity.DataBindingMode = (int) Mode;
|
|
||||||
Entity.EasingTime = EasingTime;
|
|
||||||
Entity.EasingFunction = (int) EasingFunction;
|
|
||||||
|
|
||||||
// Data model
|
|
||||||
Entity.SourceDataModelGuid = SourceDataModel?.PluginInfo?.Guid;
|
|
||||||
Entity.SourcePropertyPath = SourcePropertyPath;
|
|
||||||
|
|
||||||
// Modifiers
|
|
||||||
Entity.Modifiers.Clear();
|
|
||||||
foreach (var dataBindingModifier in Modifiers)
|
|
||||||
{
|
|
||||||
dataBindingModifier.ApplyToEntity();
|
|
||||||
Entity.Modifiers.Add(dataBindingModifier.Entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ApplyToDataBinding()
|
|
||||||
{
|
|
||||||
// General
|
|
||||||
ApplyRegistration(LayerProperty.DataBindingRegistrations.FirstOrDefault(p => p.Property.Name == Entity.TargetProperty));
|
|
||||||
|
|
||||||
Mode = (DataBindingMode) Entity.DataBindingMode;
|
|
||||||
EasingTime = Entity.EasingTime;
|
|
||||||
EasingFunction = (Easings.Functions) Entity.EasingFunction;
|
|
||||||
|
|
||||||
// Data model is done during Initialize
|
|
||||||
|
|
||||||
// Modifiers
|
|
||||||
foreach (var dataBindingModifierEntity in Entity.Modifiers)
|
|
||||||
_modifiers.Add(new DataBindingModifier(this, dataBindingModifierEntity));
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Initialize(IDataModelService dataModelService, IDataBindingService dataBindingService)
|
|
||||||
{
|
|
||||||
// Source
|
// Source
|
||||||
if (Entity.SourceDataModelGuid != null && SourceDataModel == null)
|
if (Entity.SourceDataModelGuid != null && SourceDataModel == null)
|
||||||
{
|
{
|
||||||
var dataModel = dataModelService.GetPluginDataModelByGuid(Entity.SourceDataModelGuid.Value);
|
var dataModel = DataModelStore.Get(Entity.SourceDataModelGuid.Value)?.DataModel;
|
||||||
if (dataModel != null && dataModel.ContainsPath(Entity.SourcePropertyPath))
|
if (dataModel != null && dataModel.ContainsPath(Entity.SourcePropertyPath))
|
||||||
UpdateSource(dataModel, Entity.SourcePropertyPath);
|
UpdateSource(dataModel, Entity.SourcePropertyPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modifiers
|
|
||||||
foreach (var dataBindingModifier in Modifiers)
|
|
||||||
dataBindingModifier.Initialize(dataModelService, dataBindingService);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyRegistration(DataBindingRegistration dataBindingRegistration)
|
private void ResetEasing(TProperty value)
|
||||||
|
{
|
||||||
|
_previousValue = GetInterpolatedValue();
|
||||||
|
_currentValue = value;
|
||||||
|
_easingProgress = TimeSpan.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyRegistration(DataBindingRegistration<TLayerProperty, TProperty> dataBindingRegistration)
|
||||||
{
|
{
|
||||||
if (dataBindingRegistration != null)
|
if (dataBindingRegistration != null)
|
||||||
dataBindingRegistration.DataBinding = this;
|
dataBindingRegistration.DataBinding = this;
|
||||||
|
|
||||||
Converter = dataBindingRegistration?.Converter;
|
Converter = dataBindingRegistration?.Converter;
|
||||||
Registration = dataBindingRegistration;
|
Registration = dataBindingRegistration;
|
||||||
TargetProperty = dataBindingRegistration?.Property;
|
|
||||||
|
|
||||||
if (GetTargetType().IsValueType)
|
if (GetTargetType().IsValueType)
|
||||||
{
|
{
|
||||||
if (_currentValue == null)
|
if (_currentValue == null)
|
||||||
_currentValue = GetTargetType().GetDefault();
|
_currentValue = default;
|
||||||
if (_previousValue == null)
|
if (_previousValue == null)
|
||||||
_previousValue = _currentValue;
|
_previousValue = default;
|
||||||
}
|
}
|
||||||
|
|
||||||
Converter?.Initialize(this);
|
Converter?.Initialize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private object GetInterpolatedValue()
|
private TProperty GetInterpolatedValue()
|
||||||
{
|
{
|
||||||
if (_easingProgress == EasingTime || !Converter.SupportsInterpolate)
|
if (_easingProgress == EasingTime || !Converter.SupportsInterpolate)
|
||||||
return _currentValue;
|
return _currentValue;
|
||||||
@ -318,6 +303,77 @@ namespace Artemis.Core
|
|||||||
CompiledTargetAccessor = lambda.Compile();
|
CompiledTargetAccessor = lambda.Compile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Storage
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Load()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("DataBinding");
|
||||||
|
// General
|
||||||
|
var registration = LayerProperty.GetDataBindingRegistration<TProperty>(Entity.TargetExpression);
|
||||||
|
ApplyRegistration(registration);
|
||||||
|
|
||||||
|
Mode = (DataBindingMode) Entity.DataBindingMode;
|
||||||
|
EasingTime = Entity.EasingTime;
|
||||||
|
EasingFunction = (Easings.Functions) Entity.EasingFunction;
|
||||||
|
|
||||||
|
// Data model is done during Initialize
|
||||||
|
|
||||||
|
// Modifiers
|
||||||
|
foreach (var dataBindingModifierEntity in Entity.Modifiers)
|
||||||
|
_modifiers.Add(new DataBindingModifier<TLayerProperty, TProperty>(this, dataBindingModifierEntity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("DataBinding");
|
||||||
|
|
||||||
|
if (!LayerProperty.Entity.DataBindingEntities.Contains(Entity))
|
||||||
|
LayerProperty.Entity.DataBindingEntities.Add(Entity);
|
||||||
|
|
||||||
|
// General
|
||||||
|
Entity.TargetExpression = Registration.PropertyExpression.ToString();
|
||||||
|
Entity.DataBindingMode = (int) Mode;
|
||||||
|
Entity.EasingTime = EasingTime;
|
||||||
|
Entity.EasingFunction = (int) EasingFunction;
|
||||||
|
|
||||||
|
// Data model
|
||||||
|
if (SourceDataModel != null)
|
||||||
|
{
|
||||||
|
Entity.SourceDataModelGuid = SourceDataModel.PluginInfo.Guid;
|
||||||
|
Entity.SourcePropertyPath = SourcePropertyPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modifiers
|
||||||
|
Entity.Modifiers.Clear();
|
||||||
|
foreach (var dataBindingModifier in Modifiers)
|
||||||
|
dataBindingModifier.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Event handlers
|
||||||
|
|
||||||
|
private void DataModelStoreOnDataModelAdded(object sender, DataModelStoreEvent e)
|
||||||
|
{
|
||||||
|
var dataModel = e.Registration.DataModel;
|
||||||
|
if (dataModel.PluginInfo.Guid == Entity.SourceDataModelGuid && dataModel.ContainsPath(Entity.SourcePropertyPath))
|
||||||
|
UpdateSource(dataModel, Entity.SourcePropertyPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DataModelStoreOnDataModelRemoved(object sender, DataModelStoreEvent e)
|
||||||
|
{
|
||||||
|
if (SourceDataModel != e.Registration.DataModel)
|
||||||
|
return;
|
||||||
|
SourceDataModel = null;
|
||||||
|
CompiledTargetAccessor = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Events
|
#region Events
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -1,29 +1,31 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A data binding converter that acts as the bridge between a <see cref="DataBinding" /> and a
|
/// Represents a data binding converter that acts as the bridge between a
|
||||||
/// <see cref="LayerProperty{T}" />
|
/// <see cref="DataBinding{TLayerProperty, TProperty}" /> and a <see cref="LayerProperty{T}" />
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class DataBindingConverter
|
public abstract class DataBindingConverter<TLayerProperty, TProperty> : IDataBindingConverter
|
||||||
{
|
{
|
||||||
internal Func<object> ValueGetter { get; set; }
|
/// <summary>
|
||||||
internal Action<object> ValueSetter { get; set; }
|
/// A dynamically compiled getter pointing to the data bound property
|
||||||
|
/// </summary>
|
||||||
|
public Func<TLayerProperty, TProperty> GetExpression { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dynamically compiled setter pointing to the data bound property
|
||||||
|
/// </summary>
|
||||||
|
public Action<TProperty> ValueTypeSetExpression { get; private set; }
|
||||||
|
|
||||||
|
public Action<TLayerProperty, TProperty> ReferenceTypeSetExpression { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the data binding this converter is applied to
|
/// Gets the data binding this converter is applied to
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DataBinding DataBinding { get; private set; }
|
public DataBinding<TLayerProperty, TProperty> DataBinding { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the type this converter supports
|
|
||||||
/// </summary>
|
|
||||||
public Type SupportedType { get; protected set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether or not this data binding converter supports the <see cref="Sum" /> method
|
/// Gets whether or not this data binding converter supports the <see cref="Sum" /> method
|
||||||
@ -35,17 +37,13 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool SupportsInterpolate { get; protected set; }
|
public bool SupportsInterpolate { get; protected set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Called when the data binding converter has been initialized and the <see cref="DataBinding" /> is available
|
public Type SupportedType => typeof(TProperty);
|
||||||
/// </summary>
|
|
||||||
protected virtual void OnInitialized()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the sum of <paramref name="a" /> and <paramref name="b" />
|
/// Returns the sum of <paramref name="a" /> and <paramref name="b" />
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract object Sum(object a, object b);
|
public abstract TProperty Sum(TProperty a, TProperty b);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the the interpolated value between <paramref name="a" /> and <paramref name="b" /> on a scale (generally)
|
/// Returns the the interpolated value between <paramref name="a" /> and <paramref name="b" /> on a scale (generally)
|
||||||
@ -56,66 +54,117 @@ namespace Artemis.Core
|
|||||||
/// <param name="b">The value to interpolate towards</param>
|
/// <param name="b">The value to interpolate towards</param>
|
||||||
/// <param name="progress">The progress of the interpolation between 0.0 and 1.0</param>
|
/// <param name="progress">The progress of the interpolation between 0.0 and 1.0</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public abstract object Interpolate(object a, object b, double progress);
|
public abstract TProperty Interpolate(TProperty a, TProperty b, double progress);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Applies the <paramref name="value" /> to the layer property
|
/// Applies the <paramref name="value" /> to the layer property
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value"></param>
|
/// <param name="value"></param>
|
||||||
public abstract void ApplyValue(object value);
|
public virtual void ApplyValue(TProperty value)
|
||||||
|
{
|
||||||
|
if (ReferenceTypeSetExpression != null)
|
||||||
|
ReferenceTypeSetExpression(DataBinding.LayerProperty.CurrentValue, value);
|
||||||
|
else if (ValueTypeSetExpression != null)
|
||||||
|
ValueTypeSetExpression(value);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the current base value of the data binding
|
/// Returns the current base value of the data binding
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract object GetValue();
|
public virtual TProperty GetValue()
|
||||||
|
{
|
||||||
|
return GetExpression(DataBinding.LayerProperty.CurrentValue);
|
||||||
|
}
|
||||||
|
|
||||||
internal void Initialize(DataBinding dataBinding)
|
/// <summary>
|
||||||
|
/// Converts the provided object to a type of <typeparamref name="TProperty" />
|
||||||
|
/// </summary>
|
||||||
|
public virtual TProperty ConvertFromObject(object source)
|
||||||
|
{
|
||||||
|
return (TProperty) Convert.ChangeType(source, typeof(TProperty));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the data binding converter has been initialized and the <see cref="DataBinding" /> is available
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnInitialized()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Initialize(DataBinding<TLayerProperty, TProperty> dataBinding)
|
||||||
{
|
{
|
||||||
DataBinding = dataBinding;
|
DataBinding = dataBinding;
|
||||||
ValueGetter = CreateValueGetter();
|
GetExpression = dataBinding.Registration.PropertyExpression.Compile();
|
||||||
ValueSetter = CreateValueSetter();
|
CreateSetExpression();
|
||||||
|
|
||||||
OnInitialized();
|
OnInitialized();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Func<object> CreateValueGetter()
|
private void CreateSetExpression()
|
||||||
{
|
{
|
||||||
if (DataBinding.TargetProperty?.DeclaringType == null)
|
// If the registration does not point towards a member of LayerProperty<T>.CurrentValue, assign directly to LayerProperty<T>.CurrentValue
|
||||||
return null;
|
if (DataBinding.Registration.Member == null)
|
||||||
|
{
|
||||||
|
CreateSetCurrentValueExpression();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var getterMethod = DataBinding.TargetProperty.GetGetMethod();
|
// Ensure the member of LayerProperty<T>.CurrentValue has a setter
|
||||||
if (getterMethod == null)
|
MethodInfo setterMethod = null;
|
||||||
return null;
|
if (DataBinding.Registration.Member is PropertyInfo propertyInfo)
|
||||||
|
setterMethod = propertyInfo.GetSetMethod();
|
||||||
var constant = Expression.Constant(DataBinding.LayerProperty);
|
// If there is no setter, the built-in data binding cannot do its job, stay null
|
||||||
// The path is null if the registration is applied to the root (LayerProperty.CurrentValue)
|
if (setterMethod == null)
|
||||||
var property = DataBinding.Registration.Path == null
|
return;
|
||||||
? Expression.Property(constant, DataBinding.TargetProperty)
|
|
||||||
: (MemberExpression) DataBinding.Registration.Path.Split('.').Aggregate<string, Expression>(constant, Expression.Property);
|
|
||||||
|
|
||||||
// The get method should cast to the object since it receives whatever type the property is
|
// If LayerProperty<T>.CurrentValue is a value type, assign it directly to LayerProperty<T>.CurrentValue after applying the changes
|
||||||
var body = Expression.Convert(property, typeof(object));
|
if (typeof(TLayerProperty).IsValueType)
|
||||||
var lambda = Expression.Lambda<Func<object>>(body);
|
CreateSetValueTypeExpression();
|
||||||
|
// If it is a reference type it can safely be updated by its reference
|
||||||
return lambda.Compile();
|
else
|
||||||
|
CreateSetReferenceTypeExpression();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Action<object> CreateValueSetter()
|
private void CreateSetReferenceTypeExpression()
|
||||||
{
|
{
|
||||||
if (DataBinding.TargetProperty?.DeclaringType == null)
|
var propertyValue = Expression.Parameter(typeof(TProperty), "propertyValue");
|
||||||
return null;
|
var parameter = Expression.Parameter(typeof(TLayerProperty), "currentValue");
|
||||||
|
var memberAccess = Expression.MakeMemberAccess(parameter, DataBinding.Registration.Member);
|
||||||
|
var assignment = Expression.Assign(memberAccess, propertyValue);
|
||||||
|
var referenceTypeLambda = Expression.Lambda<Action<TLayerProperty, TProperty>>(assignment, parameter, propertyValue);
|
||||||
|
|
||||||
var setterMethod = DataBinding.TargetProperty.GetSetMethod();
|
ReferenceTypeSetExpression = referenceTypeLambda.Compile();
|
||||||
if (setterMethod == null)
|
}
|
||||||
return null;
|
|
||||||
|
|
||||||
var constant = Expression.Constant(DataBinding.LayerProperty);
|
private void CreateSetValueTypeExpression()
|
||||||
var propertyValue = Expression.Parameter(typeof(object), "propertyValue");
|
{
|
||||||
|
var propertyValue = Expression.Parameter(typeof(TProperty), "propertyValue");
|
||||||
|
var variableCurrent = Expression.Variable(typeof(TLayerProperty), "current");
|
||||||
|
var layerProperty = Expression.Constant(DataBinding.LayerProperty);
|
||||||
|
var layerPropertyMemberAccess = Expression.MakeMemberAccess(layerProperty,
|
||||||
|
DataBinding.LayerProperty.GetType().GetMember(nameof(DataBinding.LayerProperty.CurrentValue))[0]);
|
||||||
|
|
||||||
// The assign method should cast to the proper type since it receives an object
|
var body = Expression.Block(
|
||||||
var body = Expression.Call(constant, setterMethod, Expression.Convert(propertyValue, DataBinding.TargetProperty.PropertyType));
|
new[] {variableCurrent},
|
||||||
var lambda = Expression.Lambda<Action<object>>(body, propertyValue);
|
Expression.Assign(variableCurrent, layerPropertyMemberAccess),
|
||||||
return lambda.Compile();
|
Expression.Assign(Expression.MakeMemberAccess(variableCurrent, DataBinding.Registration.Member), propertyValue),
|
||||||
|
Expression.Assign(layerPropertyMemberAccess, variableCurrent)
|
||||||
|
);
|
||||||
|
|
||||||
|
var valueTypeLambda = Expression.Lambda<Action<TProperty>>(body, propertyValue);
|
||||||
|
ValueTypeSetExpression = valueTypeLambda.Compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateSetCurrentValueExpression()
|
||||||
|
{
|
||||||
|
var propertyValue = Expression.Parameter(typeof(TProperty), "propertyValue");
|
||||||
|
var layerProperty = Expression.Constant(DataBinding.LayerProperty);
|
||||||
|
var layerPropertyMemberAccess = Expression.MakeMemberAccess(layerProperty,
|
||||||
|
DataBinding.LayerProperty.GetType().GetMember(nameof(DataBinding.LayerProperty.CurrentValue))[0]);
|
||||||
|
|
||||||
|
var body = Expression.Assign(layerPropertyMemberAccess, propertyValue);
|
||||||
|
var lambda = Expression.Lambda<Action<TProperty>>(body, propertyValue);
|
||||||
|
ValueTypeSetExpression = lambda.Compile();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2,43 +2,43 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using Artemis.Core.DataModelExpansions;
|
using Artemis.Core.DataModelExpansions;
|
||||||
using Artemis.Core.Services;
|
|
||||||
using Artemis.Storage.Entities.Profile.DataBindings;
|
using Artemis.Storage.Entities.Profile.DataBindings;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Modifies a data model value in a way defined by the modifier type
|
public class DataBindingModifier<TLayerProperty, TProperty> : IDataBindingModifier
|
||||||
/// </summary>
|
|
||||||
public class DataBindingModifier
|
|
||||||
{
|
{
|
||||||
private DataBinding _dataBinding;
|
private DataBinding<TLayerProperty, TProperty> _dataBinding;
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the <see cref="DataBindingModifier" /> class
|
/// Creates a new instance of the <see cref="DataBindingModifier{TLayerProperty,TProperty}" /> class
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="dataBinding">The data binding the modifier is to be applied to</param>
|
||||||
/// <param name="parameterType">The type of the parameter, can either be dynamic (based on a data model value) or static</param>
|
/// <param name="parameterType">The type of the parameter, can either be dynamic (based on a data model value) or static</param>
|
||||||
public DataBindingModifier(ProfileRightSideType parameterType)
|
public DataBindingModifier(DataBinding<TLayerProperty, TProperty> dataBinding, ProfileRightSideType parameterType)
|
||||||
{
|
{
|
||||||
|
_dataBinding = dataBinding ?? throw new ArgumentNullException(nameof(dataBinding));
|
||||||
ParameterType = parameterType;
|
ParameterType = parameterType;
|
||||||
Entity = new DataBindingModifierEntity();
|
Entity = new DataBindingModifierEntity();
|
||||||
|
Initialize();
|
||||||
ApplyToEntity();
|
Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal DataBindingModifier(DataBinding dataBinding, DataBindingModifierEntity entity)
|
internal DataBindingModifier(DataBinding<TLayerProperty, TProperty> dataBinding, DataBindingModifierEntity entity)
|
||||||
{
|
{
|
||||||
DataBinding = dataBinding;
|
_dataBinding = dataBinding;
|
||||||
Entity = entity;
|
Entity = entity;
|
||||||
|
Load();
|
||||||
ApplyToDataBindingModifier();
|
Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the data binding this modifier is applied to
|
/// Gets the data binding this modifier is applied to
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DataBinding DataBinding
|
public DataBinding<TLayerProperty, TProperty> DataBinding
|
||||||
{
|
{
|
||||||
get => _dataBinding;
|
get => _dataBinding;
|
||||||
internal set
|
internal set
|
||||||
@ -86,6 +86,52 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
internal DataBindingModifierEntity Entity { get; set; }
|
internal DataBindingModifierEntity Entity { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("DataBindingModifier");
|
||||||
|
|
||||||
|
if (!DataBinding.Entity.Modifiers.Contains(Entity))
|
||||||
|
DataBinding.Entity.Modifiers.Add(Entity);
|
||||||
|
|
||||||
|
// Modifier
|
||||||
|
if (ModifierType != null)
|
||||||
|
{
|
||||||
|
Entity.ModifierType = ModifierType.GetType().Name;
|
||||||
|
Entity.ModifierTypePluginGuid = ModifierType.PluginInfo.Guid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// General
|
||||||
|
Entity.Order = Order;
|
||||||
|
Entity.ParameterType = (int) ParameterType;
|
||||||
|
|
||||||
|
// Parameter
|
||||||
|
if (ParameterDataModel != null)
|
||||||
|
{
|
||||||
|
Entity.ParameterDataModelGuid = ParameterDataModel.PluginInfo.Guid;
|
||||||
|
Entity.ParameterPropertyPath = ParameterPropertyPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity.ParameterStaticValue = JsonConvert.SerializeObject(ParameterStaticValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Load()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("DataBindingModifier");
|
||||||
|
|
||||||
|
// Modifier type is done during Initialize
|
||||||
|
|
||||||
|
// General
|
||||||
|
Order = Entity.Order;
|
||||||
|
ParameterType = (ProfileRightSideType) Entity.ParameterType;
|
||||||
|
|
||||||
|
// Parameter is done during initialize
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Applies the modifier to the provided value
|
/// Applies the modifier to the provided value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -93,6 +139,9 @@ namespace Artemis.Core
|
|||||||
/// <returns>The modified value</returns>
|
/// <returns>The modified value</returns>
|
||||||
public object Apply(object currentValue)
|
public object Apply(object currentValue)
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("DataBindingModifier");
|
||||||
|
|
||||||
if (ModifierType == null)
|
if (ModifierType == null)
|
||||||
return currentValue;
|
return currentValue;
|
||||||
|
|
||||||
@ -117,6 +166,9 @@ namespace Artemis.Core
|
|||||||
/// <param name="modifierType"></param>
|
/// <param name="modifierType"></param>
|
||||||
public void UpdateModifierType(DataBindingModifierType modifierType)
|
public void UpdateModifierType(DataBindingModifierType modifierType)
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("DataBindingModifier");
|
||||||
|
|
||||||
// Calling CreateExpression will clear compiled expressions
|
// Calling CreateExpression will clear compiled expressions
|
||||||
if (modifierType == null)
|
if (modifierType == null)
|
||||||
{
|
{
|
||||||
@ -125,7 +177,7 @@ namespace Artemis.Core
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var targetType = DataBinding.TargetProperty.PropertyType;
|
var targetType = DataBinding.GetTargetType();
|
||||||
if (!modifierType.SupportsType(targetType))
|
if (!modifierType.SupportsType(targetType))
|
||||||
{
|
{
|
||||||
throw new ArtemisCoreException($"Cannot apply modifier type {modifierType.GetType().Name} to this modifier because " +
|
throw new ArtemisCoreException($"Cannot apply modifier type {modifierType.GetType().Name} to this modifier because " +
|
||||||
@ -143,6 +195,9 @@ namespace Artemis.Core
|
|||||||
/// <param name="path">The path pointing to the parameter inside the data model</param>
|
/// <param name="path">The path pointing to the parameter inside the data model</param>
|
||||||
public void UpdateParameter(DataModel dataModel, string path)
|
public void UpdateParameter(DataModel dataModel, string path)
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("DataBindingModifier");
|
||||||
|
|
||||||
if (dataModel != null && path == null)
|
if (dataModel != null && path == null)
|
||||||
throw new ArtemisCoreException("If a data model is provided, a path is also required");
|
throw new ArtemisCoreException("If a data model is provided, a path is also required");
|
||||||
if (dataModel == null && path != null)
|
if (dataModel == null && path != null)
|
||||||
@ -167,11 +222,14 @@ namespace Artemis.Core
|
|||||||
/// <param name="staticValue">The static value to use as a parameter</param>
|
/// <param name="staticValue">The static value to use as a parameter</param>
|
||||||
public void UpdateParameter(object staticValue)
|
public void UpdateParameter(object staticValue)
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("DataBindingModifier");
|
||||||
|
|
||||||
ParameterType = ProfileRightSideType.Static;
|
ParameterType = ProfileRightSideType.Static;
|
||||||
ParameterDataModel = null;
|
ParameterDataModel = null;
|
||||||
ParameterPropertyPath = null;
|
ParameterPropertyPath = null;
|
||||||
|
|
||||||
var targetType = DataBinding.TargetProperty.PropertyType;
|
var targetType = DataBinding.GetTargetType();
|
||||||
|
|
||||||
// If not null ensure the types match and if not, convert it
|
// If not null ensure the types match and if not, convert it
|
||||||
if (staticValue != null && staticValue.GetType() == targetType)
|
if (staticValue != null && staticValue.GetType() == targetType)
|
||||||
@ -187,12 +245,17 @@ namespace Artemis.Core
|
|||||||
CreateExpression();
|
CreateExpression();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Initialize(IDataModelService dataModelService, IDataBindingService dataBindingService)
|
private void Initialize()
|
||||||
{
|
{
|
||||||
|
DataBindingModifierTypeStore.DataBindingModifierAdded += DataBindingModifierTypeStoreOnDataBindingModifierAdded;
|
||||||
|
DataBindingModifierTypeStore.DataBindingModifierRemoved += DataBindingModifierTypeStoreOnDataBindingModifierRemoved;
|
||||||
|
DataModelStore.DataModelAdded += DataModelStoreOnDataModelAdded;
|
||||||
|
DataModelStore.DataModelRemoved += DataModelStoreOnDataModelRemoved;
|
||||||
|
|
||||||
// Modifier type
|
// Modifier type
|
||||||
if (Entity.ModifierTypePluginGuid != null && ModifierType == null)
|
if (Entity.ModifierTypePluginGuid != null && ModifierType == null)
|
||||||
{
|
{
|
||||||
var modifierType = dataBindingService.GetModifierType(Entity.ModifierTypePluginGuid.Value, Entity.ModifierType);
|
var modifierType = DataBindingModifierTypeStore.Get(Entity.ModifierTypePluginGuid.Value, Entity.ModifierType)?.DataBindingModifierType;
|
||||||
if (modifierType != null)
|
if (modifierType != null)
|
||||||
UpdateModifierType(modifierType);
|
UpdateModifierType(modifierType);
|
||||||
}
|
}
|
||||||
@ -200,7 +263,7 @@ namespace Artemis.Core
|
|||||||
// Dynamic parameter
|
// Dynamic parameter
|
||||||
if (ParameterType == ProfileRightSideType.Dynamic && Entity.ParameterDataModelGuid != null && ParameterDataModel == null)
|
if (ParameterType == ProfileRightSideType.Dynamic && Entity.ParameterDataModelGuid != null && ParameterDataModel == null)
|
||||||
{
|
{
|
||||||
var dataModel = dataModelService.GetPluginDataModelByGuid(Entity.ParameterDataModelGuid.Value);
|
var dataModel = DataModelStore.Get(Entity.ParameterDataModelGuid.Value)?.DataModel;
|
||||||
if (dataModel != null && dataModel.ContainsPath(Entity.ParameterPropertyPath))
|
if (dataModel != null && dataModel.ContainsPath(Entity.ParameterPropertyPath))
|
||||||
UpdateParameter(dataModel, Entity.ParameterPropertyPath);
|
UpdateParameter(dataModel, Entity.ParameterPropertyPath);
|
||||||
}
|
}
|
||||||
@ -208,7 +271,7 @@ namespace Artemis.Core
|
|||||||
else if (ParameterType == ProfileRightSideType.Static && Entity.ParameterStaticValue != null && ParameterStaticValue == null)
|
else if (ParameterType == ProfileRightSideType.Static && Entity.ParameterStaticValue != null && ParameterStaticValue == null)
|
||||||
{
|
{
|
||||||
// Use the target type so JSON.NET has a better idea what to do
|
// Use the target type so JSON.NET has a better idea what to do
|
||||||
var targetType = DataBinding.TargetProperty.PropertyType;
|
var targetType = DataBinding.GetTargetType();
|
||||||
object staticValue;
|
object staticValue;
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -218,7 +281,7 @@ namespace Artemis.Core
|
|||||||
// If deserialization fails, use the type's default
|
// If deserialization fails, use the type's default
|
||||||
catch (JsonSerializationException e)
|
catch (JsonSerializationException e)
|
||||||
{
|
{
|
||||||
dataBindingService.LogModifierDeserializationFailure(this, e);
|
DeserializationLogger.LogModifierDeserializationFailure(GetType().Name, e);
|
||||||
staticValue = Activator.CreateInstance(targetType);
|
staticValue = Activator.CreateInstance(targetType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,34 +289,6 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void ApplyToEntity()
|
|
||||||
{
|
|
||||||
// Modifier
|
|
||||||
Entity.ModifierType = ModifierType?.GetType().Name;
|
|
||||||
Entity.ModifierTypePluginGuid = ModifierType?.PluginInfo.Guid;
|
|
||||||
|
|
||||||
// General
|
|
||||||
Entity.Order = Order;
|
|
||||||
Entity.ParameterType = (int) ParameterType;
|
|
||||||
|
|
||||||
// Parameter
|
|
||||||
Entity.ParameterDataModelGuid = ParameterDataModel?.PluginInfo.Guid;
|
|
||||||
Entity.ParameterPropertyPath = ParameterPropertyPath;
|
|
||||||
Entity.ParameterStaticValue = JsonConvert.SerializeObject(ParameterStaticValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ApplyToDataBindingModifier()
|
|
||||||
{
|
|
||||||
// Modifier type is done during Initialize
|
|
||||||
|
|
||||||
// General
|
|
||||||
Order = Entity.Order;
|
|
||||||
ParameterType = (ProfileRightSideType) Entity.ParameterType;
|
|
||||||
|
|
||||||
// Parameter is done during initialize
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void CreateExpression()
|
private void CreateExpression()
|
||||||
{
|
{
|
||||||
CompiledParameterAccessor = null;
|
CompiledParameterAccessor = null;
|
||||||
@ -285,5 +320,51 @@ namespace Artemis.Core
|
|||||||
Expression.Property
|
Expression.Property
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Event handlers
|
||||||
|
|
||||||
|
private void DataBindingModifierTypeStoreOnDataBindingModifierAdded(object sender, DataBindingModifierTypeStoreEvent e)
|
||||||
|
{
|
||||||
|
if (ModifierType != null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var modifierType = e.TypeRegistration.DataBindingModifierType;
|
||||||
|
if (modifierType.PluginInfo.Guid == Entity.ModifierTypePluginGuid && modifierType.GetType().Name == Entity.ModifierType)
|
||||||
|
UpdateModifierType(modifierType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DataBindingModifierTypeStoreOnDataBindingModifierRemoved(object sender, DataBindingModifierTypeStoreEvent e)
|
||||||
|
{
|
||||||
|
if (e.TypeRegistration.DataBindingModifierType == ModifierType)
|
||||||
|
UpdateModifierType(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DataModelStoreOnDataModelAdded(object sender, DataModelStoreEvent e)
|
||||||
|
{
|
||||||
|
var dataModel = e.Registration.DataModel;
|
||||||
|
if (dataModel.PluginInfo.Guid == Entity.ParameterDataModelGuid && dataModel.ContainsPath(Entity.ParameterPropertyPath))
|
||||||
|
UpdateParameter(dataModel, Entity.ParameterPropertyPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DataModelStoreOnDataModelRemoved(object sender, DataModelStoreEvent e)
|
||||||
|
{
|
||||||
|
if (e.Registration.DataModel != ParameterDataModel)
|
||||||
|
return;
|
||||||
|
ParameterDataModel = null;
|
||||||
|
CompiledParameterAccessor = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_disposed = true;
|
||||||
|
|
||||||
|
DataBindingModifierTypeStore.DataBindingModifierAdded -= DataBindingModifierTypeStoreOnDataBindingModifierAdded;
|
||||||
|
DataBindingModifierTypeStore.DataBindingModifierRemoved -= DataBindingModifierTypeStoreOnDataBindingModifierRemoved;
|
||||||
|
DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded;
|
||||||
|
DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,22 +1,63 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
public class DataBindingRegistration
|
/// <inheritdoc />
|
||||||
|
public class DataBindingRegistration<TLayerProperty, TProperty> : IDataBindingRegistration
|
||||||
{
|
{
|
||||||
internal DataBindingRegistration(BaseLayerProperty layerProperty, PropertyInfo property, DataBindingConverter converter, string path)
|
internal DataBindingRegistration(LayerProperty<TLayerProperty> layerProperty,
|
||||||
|
DataBindingConverter<TLayerProperty, TProperty> converter,
|
||||||
|
Expression<Func<TLayerProperty, TProperty>> propertyExpression)
|
||||||
{
|
{
|
||||||
LayerProperty = layerProperty ?? throw new ArgumentNullException(nameof(layerProperty));
|
LayerProperty = layerProperty ?? throw new ArgumentNullException(nameof(layerProperty));
|
||||||
Property = property ?? throw new ArgumentNullException(nameof(property));
|
|
||||||
Converter = converter ?? throw new ArgumentNullException(nameof(converter));
|
Converter = converter ?? throw new ArgumentNullException(nameof(converter));
|
||||||
Path = path;
|
PropertyExpression = propertyExpression ?? throw new ArgumentNullException(nameof(propertyExpression));
|
||||||
|
|
||||||
|
if (propertyExpression.Body is MemberExpression memberExpression)
|
||||||
|
Member = memberExpression.Member;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataBinding DataBinding { get; internal set; }
|
/// <summary>
|
||||||
public BaseLayerProperty LayerProperty { get; }
|
/// Gets the layer property this registration was made on
|
||||||
public PropertyInfo Property { get; }
|
/// </summary>
|
||||||
public DataBindingConverter Converter { get; }
|
public LayerProperty<TLayerProperty> LayerProperty { get; }
|
||||||
public string Path { get; }
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the converter that's used by the data binding
|
||||||
|
/// </summary>
|
||||||
|
public DataBindingConverter<TLayerProperty, TProperty> Converter { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the expression that that accesses the property
|
||||||
|
/// </summary>
|
||||||
|
public Expression<Func<TLayerProperty, TProperty>> PropertyExpression { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the member the <see cref="PropertyExpression" /> targets
|
||||||
|
/// <para><c>null</c> if the <see cref="PropertyExpression" /> is not a member expression</para>
|
||||||
|
/// </summary>
|
||||||
|
public MemberInfo Member { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the data binding created using this registration
|
||||||
|
/// </summary>
|
||||||
|
public DataBinding<TLayerProperty, TProperty> DataBinding { get; internal set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IDataBinding CreateDataBinding()
|
||||||
|
{
|
||||||
|
if (DataBinding != null)
|
||||||
|
return DataBinding;
|
||||||
|
|
||||||
|
var dataBinding = LayerProperty.Entity.DataBindingEntities.FirstOrDefault(e => e.TargetExpression == PropertyExpression.ToString());
|
||||||
|
if (dataBinding == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
DataBinding = new DataBinding<TLayerProperty, TProperty>(LayerProperty, dataBinding);
|
||||||
|
return DataBinding;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
17
src/Artemis.Core/Models/Profile/DataBindings/IDataBinding.cs
Normal file
17
src/Artemis.Core/Models/Profile/DataBindings/IDataBinding.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using Artemis.Core.DataModelExpansions;
|
||||||
|
|
||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a data binding that binds a certain <see cref="LayerProperty{T}" /> to a value inside a
|
||||||
|
/// <see cref="DataModel" />
|
||||||
|
/// </summary>
|
||||||
|
public interface IDataBinding : IStorageModel, IUpdateModel, IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Applies the data binding to the layer property
|
||||||
|
/// </summary>
|
||||||
|
void Apply();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a data binding converter that acts as the bridge between a
|
||||||
|
/// <see cref="DataBinding{TLayerProperty, TProperty}" /> and a <see cref="LayerProperty{T}" />
|
||||||
|
/// </summary>
|
||||||
|
public interface IDataBindingConverter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the type this converter supports
|
||||||
|
/// </summary>
|
||||||
|
public Type SupportedType { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Modifies a data model value in a way defined by the modifier type
|
||||||
|
/// </summary>
|
||||||
|
public interface IDataBindingModifier : IStorageModel, IDisposable
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a data binding registration
|
||||||
|
/// </summary>
|
||||||
|
public interface IDataBindingRegistration
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// If found, creates a data binding from storage
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
IDataBinding CreateDataBinding();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Artemis.Core.Services;
|
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
@ -10,9 +9,6 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class DataBindingModifierType
|
public abstract class DataBindingModifierType
|
||||||
{
|
{
|
||||||
private IDataBindingService _dataBindingService;
|
|
||||||
private bool _registered;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the plugin info this data binding modifier belongs to
|
/// Gets the plugin info this data binding modifier belongs to
|
||||||
/// <para>Note: Not set until after registering</para>
|
/// <para>Note: Not set until after registering</para>
|
||||||
@ -35,10 +31,16 @@ namespace Artemis.Core
|
|||||||
public abstract string Icon { get; }
|
public abstract string Icon { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets whether this modifier supports a parameter, defaults to true
|
/// Gets or sets whether this modifier supports a parameter, defaults to <c>true</c>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool SupportsParameter { get; protected set; } = true;
|
public bool SupportsParameter { get; protected set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the preferred parameter type
|
||||||
|
/// <para>If <c>null</c>, the parameter type will match the source property</para>
|
||||||
|
/// </summary>
|
||||||
|
public Type PreferredParameterType { get; protected set; } = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns whether the given type is supported by the modifier
|
/// Returns whether the given type is supported by the modifier
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -63,35 +65,5 @@ namespace Artemis.Core
|
|||||||
/// </param>
|
/// </param>
|
||||||
/// <returns>The modified value, must be a value of a type contained in <see cref="CompatibleTypes" /></returns>
|
/// <returns>The modified value, must be a value of a type contained in <see cref="CompatibleTypes" /></returns>
|
||||||
public abstract object Apply(object currentValue, object parameterValue);
|
public abstract object Apply(object currentValue, object parameterValue);
|
||||||
|
|
||||||
internal void Register(PluginInfo pluginInfo, IDataBindingService dataBindingService)
|
|
||||||
{
|
|
||||||
if (_registered)
|
|
||||||
return;
|
|
||||||
|
|
||||||
PluginInfo = pluginInfo;
|
|
||||||
_dataBindingService = dataBindingService;
|
|
||||||
|
|
||||||
if (PluginInfo != Constants.CorePluginInfo)
|
|
||||||
PluginInfo.Instance.PluginDisabled += InstanceOnPluginDisabled;
|
|
||||||
|
|
||||||
_registered = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Unsubscribe()
|
|
||||||
{
|
|
||||||
if (!_registered)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (PluginInfo != Constants.CorePluginInfo)
|
|
||||||
PluginInfo.Instance.PluginDisabled -= InstanceOnPluginDisabled;
|
|
||||||
_registered = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InstanceOnPluginDisabled(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
// The service will call Unsubscribe
|
|
||||||
_dataBindingService.RemoveModifierType(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5,6 +5,11 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
internal class DivideModifierType : DataBindingModifierType
|
internal class DivideModifierType : DataBindingModifierType
|
||||||
{
|
{
|
||||||
|
public DivideModifierType()
|
||||||
|
{
|
||||||
|
PreferredParameterType = typeof(float);
|
||||||
|
}
|
||||||
|
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
|
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
|
||||||
|
|
||||||
public override string Description => "Divide by";
|
public override string Description => "Divide by";
|
||||||
|
|||||||
@ -5,6 +5,11 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
internal class MultiplicationModifierType : DataBindingModifierType
|
internal class MultiplicationModifierType : DataBindingModifierType
|
||||||
{
|
{
|
||||||
|
public MultiplicationModifierType()
|
||||||
|
{
|
||||||
|
PreferredParameterType = typeof(float);
|
||||||
|
}
|
||||||
|
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
|
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
|
||||||
|
|
||||||
public override string Description => "Multiply by";
|
public override string Description => "Multiply by";
|
||||||
|
|||||||
@ -8,24 +8,35 @@ using SkiaSharp;
|
|||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a folder in a <see cref="Profile" />
|
||||||
|
/// </summary>
|
||||||
public sealed class Folder : RenderProfileElement
|
public sealed class Folder : RenderProfileElement
|
||||||
{
|
{
|
||||||
private SKBitmap _folderBitmap;
|
private SKBitmap _folderBitmap;
|
||||||
|
|
||||||
public Folder(Profile profile, ProfileElement parent, string name)
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="Folder" /> class and adds itself to the child collection of the provided
|
||||||
|
/// <paramref name="parent" />
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parent">The parent of the folder</param>
|
||||||
|
/// <param name="name">The name of the folder</param>
|
||||||
|
public Folder(ProfileElement parent, string name)
|
||||||
{
|
{
|
||||||
FolderEntity = new FolderEntity();
|
FolderEntity = new FolderEntity();
|
||||||
EntityId = Guid.NewGuid();
|
EntityId = Guid.NewGuid();
|
||||||
|
|
||||||
Profile = profile;
|
Parent = parent ?? throw new ArgumentNullException(nameof(parent));
|
||||||
Parent = parent;
|
Profile = Parent.Profile;
|
||||||
Name = name;
|
Name = name;
|
||||||
Enabled = true;
|
Enabled = true;
|
||||||
DisplayContinuously = true;
|
DisplayContinuously = true;
|
||||||
|
|
||||||
_layerEffects = new List<BaseLayerEffect>();
|
_layerEffects = new List<BaseLayerEffect>();
|
||||||
_expandedPropertyGroups = new List<string>();
|
_expandedPropertyGroups = new List<string>();
|
||||||
|
|
||||||
ApplyRenderElementDefaults();
|
ApplyRenderElementDefaults();
|
||||||
|
Parent.AddChild(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Folder(Profile profile, ProfileElement parent, FolderEntity folderEntity)
|
internal Folder(Profile profile, ProfileElement parent, FolderEntity folderEntity)
|
||||||
@ -42,21 +53,7 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
_layerEffects = new List<BaseLayerEffect>();
|
_layerEffects = new List<BaseLayerEffect>();
|
||||||
_expandedPropertyGroups = new List<string>();
|
_expandedPropertyGroups = new List<string>();
|
||||||
_expandedPropertyGroups.AddRange(folderEntity.ExpandedPropertyGroups);
|
Load();
|
||||||
|
|
||||||
// Load child folders
|
|
||||||
foreach (var childFolder in Profile.ProfileEntity.Folders.Where(f => f.ParentId == EntityId))
|
|
||||||
ChildrenList.Add(new Folder(profile, this, childFolder));
|
|
||||||
// Load child layers
|
|
||||||
foreach (var childLayer in Profile.ProfileEntity.Layers.Where(f => f.ParentId == EntityId))
|
|
||||||
ChildrenList.Add(new Layer(profile, this, childLayer));
|
|
||||||
|
|
||||||
// Ensure order integrity, should be unnecessary but no one is perfect specially me
|
|
||||||
ChildrenList = ChildrenList.OrderBy(c => c.Order).ToList();
|
|
||||||
for (var index = 0; index < ChildrenList.Count; index++)
|
|
||||||
ChildrenList[index].Order = index + 1;
|
|
||||||
|
|
||||||
ApplyRenderElementEntity();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal FolderEntity FolderEntity { get; set; }
|
internal FolderEntity FolderEntity { get; set; }
|
||||||
@ -197,21 +194,6 @@ namespace Artemis.Core
|
|||||||
canvas.Restore();
|
canvas.Restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a new folder to the bottom of this folder
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public Folder AddFolder(string name)
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException("Folder");
|
|
||||||
|
|
||||||
var folder = new Folder(Profile, this, name) {Order = Children.LastOrDefault()?.Order ?? 1};
|
|
||||||
AddChild(folder);
|
|
||||||
return folder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void AddChild(ProfileElement child, int? order = null)
|
public override void AddChild(ProfileElement child, int? order = null)
|
||||||
{
|
{
|
||||||
@ -260,26 +242,35 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (!disposing)
|
_disposed = true;
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var baseLayerEffect in LayerEffects)
|
|
||||||
baseLayerEffect.Dispose();
|
|
||||||
_layerEffects.Clear();
|
|
||||||
|
|
||||||
foreach (var profileElement in Children)
|
foreach (var profileElement in Children)
|
||||||
profileElement.Dispose();
|
profileElement.Dispose();
|
||||||
ChildrenList.Clear();
|
|
||||||
|
|
||||||
_folderBitmap?.Dispose();
|
_folderBitmap?.Dispose();
|
||||||
_folderBitmap = null;
|
base.Dispose(disposing);
|
||||||
|
|
||||||
Profile = null;
|
|
||||||
_disposed = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal override void Load()
|
||||||
|
{
|
||||||
|
_expandedPropertyGroups.AddRange(FolderEntity.ExpandedPropertyGroups);
|
||||||
|
|
||||||
internal override void ApplyToEntity()
|
// Load child folders
|
||||||
|
foreach (var childFolder in Profile.ProfileEntity.Folders.Where(f => f.ParentId == EntityId))
|
||||||
|
ChildrenList.Add(new Folder(Profile, this, childFolder));
|
||||||
|
// Load child layers
|
||||||
|
foreach (var childLayer in Profile.ProfileEntity.Layers.Where(f => f.ParentId == EntityId))
|
||||||
|
ChildrenList.Add(new Layer(Profile, this, childLayer));
|
||||||
|
|
||||||
|
// Ensure order integrity, should be unnecessary but no one is perfect specially me
|
||||||
|
ChildrenList = ChildrenList.OrderBy(c => c.Order).ToList();
|
||||||
|
for (var index = 0; index < ChildrenList.Count; index++)
|
||||||
|
ChildrenList[index].Order = index + 1;
|
||||||
|
|
||||||
|
LoadRenderElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void Save()
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
throw new ObjectDisposedException("Folder");
|
throw new ObjectDisposedException("Folder");
|
||||||
@ -295,11 +286,7 @@ namespace Artemis.Core
|
|||||||
FolderEntity.ExpandedPropertyGroups.Clear();
|
FolderEntity.ExpandedPropertyGroups.Clear();
|
||||||
FolderEntity.ExpandedPropertyGroups.AddRange(_expandedPropertyGroups);
|
FolderEntity.ExpandedPropertyGroups.AddRange(_expandedPropertyGroups);
|
||||||
|
|
||||||
ApplyRenderElementToEntity();
|
SaveRenderElement();
|
||||||
|
|
||||||
// Conditions
|
|
||||||
RenderElementEntity.RootDisplayCondition = DisplayConditionGroup?.Entity;
|
|
||||||
DisplayConditionGroup?.ApplyToEntity();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Events
|
#region Events
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Artemis.Core.LayerBrushes;
|
using Artemis.Core.LayerBrushes;
|
||||||
using Artemis.Core.LayerEffects;
|
using Artemis.Core.LayerEffects;
|
||||||
using Artemis.Core.Services;
|
|
||||||
using Artemis.Storage.Entities.Profile;
|
using Artemis.Storage.Entities.Profile;
|
||||||
using Artemis.Storage.Entities.Profile.Abstract;
|
using Artemis.Storage.Entities.Profile.Abstract;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
@ -12,8 +12,7 @@ using SkiaSharp;
|
|||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a layer on a profile. To create new layers use the <see cref="RenderElementService" /> by injecting
|
/// Represents a layer in a <see cref="Profile" />
|
||||||
/// <see cref="IRenderElementService" /> into your code
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class Layer : RenderProfileElement
|
public sealed class Layer : RenderProfileElement
|
||||||
{
|
{
|
||||||
@ -24,47 +23,49 @@ namespace Artemis.Core
|
|||||||
private List<ArtemisLed> _leds;
|
private List<ArtemisLed> _leds;
|
||||||
private LayerTransformProperties _transform;
|
private LayerTransformProperties _transform;
|
||||||
|
|
||||||
internal Layer(Profile profile, ProfileElement parent, string name)
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="Layer" /> class and adds itself to the child collection of the provided
|
||||||
|
/// <paramref name="parent" />
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parent">The parent of the layer</param>
|
||||||
|
/// <param name="name">The name of the layer</param>
|
||||||
|
public Layer(ProfileElement parent, string name)
|
||||||
{
|
{
|
||||||
LayerEntity = new LayerEntity();
|
LayerEntity = new LayerEntity();
|
||||||
EntityId = Guid.NewGuid();
|
EntityId = Guid.NewGuid();
|
||||||
|
|
||||||
Profile = profile;
|
Parent = parent ?? throw new ArgumentNullException(nameof(parent));
|
||||||
Parent = parent;
|
Profile = Parent.Profile;
|
||||||
Name = name;
|
Name = name;
|
||||||
Enabled = true;
|
Enabled = true;
|
||||||
DisplayContinuously = true;
|
DisplayContinuously = true;
|
||||||
General = new LayerGeneralProperties {IsCorePropertyGroup = true};
|
General = new LayerGeneralProperties();
|
||||||
Transform = new LayerTransformProperties {IsCorePropertyGroup = true};
|
Transform = new LayerTransformProperties();
|
||||||
|
|
||||||
_layerEffects = new List<BaseLayerEffect>();
|
_layerEffects = new List<BaseLayerEffect>();
|
||||||
_leds = new List<ArtemisLed>();
|
_leds = new List<ArtemisLed>();
|
||||||
_expandedPropertyGroups = new List<string>();
|
_expandedPropertyGroups = new List<string>();
|
||||||
|
|
||||||
General.PropertyGroupInitialized += GeneralOnPropertyGroupInitialized;
|
Initialize();
|
||||||
ApplyRenderElementDefaults();
|
ApplyRenderElementDefaults();
|
||||||
|
|
||||||
|
Parent.AddChild(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Layer(Profile profile, ProfileElement parent, LayerEntity layerEntity)
|
internal Layer(Profile profile, ProfileElement parent, LayerEntity layerEntity)
|
||||||
{
|
{
|
||||||
LayerEntity = layerEntity;
|
LayerEntity = layerEntity;
|
||||||
EntityId = layerEntity.Id;
|
|
||||||
|
|
||||||
Profile = profile;
|
Profile = profile;
|
||||||
Parent = parent;
|
Parent = parent;
|
||||||
Name = layerEntity.Name;
|
General = new LayerGeneralProperties();
|
||||||
Enabled = layerEntity.Enabled;
|
Transform = new LayerTransformProperties();
|
||||||
Order = layerEntity.Order;
|
|
||||||
General = new LayerGeneralProperties {IsCorePropertyGroup = true};
|
|
||||||
Transform = new LayerTransformProperties {IsCorePropertyGroup = true};
|
|
||||||
|
|
||||||
_layerEffects = new List<BaseLayerEffect>();
|
_layerEffects = new List<BaseLayerEffect>();
|
||||||
_leds = new List<ArtemisLed>();
|
_leds = new List<ArtemisLed>();
|
||||||
_expandedPropertyGroups = new List<string>();
|
_expandedPropertyGroups = new List<string>();
|
||||||
_expandedPropertyGroups.AddRange(layerEntity.ExpandedPropertyGroups);
|
|
||||||
|
|
||||||
General.PropertyGroupInitialized += GeneralOnPropertyGroupInitialized;
|
Load();
|
||||||
ApplyRenderElementEntity();
|
Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal LayerEntity LayerEntity { get; set; }
|
internal LayerEntity LayerEntity { get; set; }
|
||||||
@ -118,56 +119,63 @@ namespace Artemis.Core
|
|||||||
return $"[Layer] {nameof(Name)}: {Name}, {nameof(Order)}: {Order}";
|
return $"[Layer] {nameof(Name)}: {Name}, {nameof(Order)}: {Order}";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
#region IDisposable
|
||||||
public override List<BaseLayerPropertyKeyframe> GetAllKeyframes()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException("Layer");
|
|
||||||
|
|
||||||
var keyframes = base.GetAllKeyframes();
|
|
||||||
|
|
||||||
foreach (var baseLayerProperty in General.GetAllLayerProperties())
|
|
||||||
keyframes.AddRange(baseLayerProperty.BaseKeyframes);
|
|
||||||
foreach (var baseLayerProperty in Transform.GetAllLayerProperties())
|
|
||||||
keyframes.AddRange(baseLayerProperty.BaseKeyframes);
|
|
||||||
if (LayerBrush?.BaseProperties != null)
|
|
||||||
{
|
|
||||||
foreach (var baseLayerProperty in LayerBrush.BaseProperties.GetAllLayerProperties())
|
|
||||||
keyframes.AddRange(baseLayerProperty.BaseKeyframes);
|
|
||||||
}
|
|
||||||
|
|
||||||
return keyframes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (!disposing)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
|
|
||||||
// Brush first in case it depends on any of the other disposables during it's own disposal
|
// Brush first in case it depends on any of the other disposables during it's own disposal
|
||||||
_layerBrush?.Dispose();
|
_layerBrush?.Dispose();
|
||||||
_layerBrush = null;
|
|
||||||
|
|
||||||
foreach (var baseLayerEffect in LayerEffects)
|
|
||||||
baseLayerEffect.Dispose();
|
|
||||||
_layerEffects.Clear();
|
|
||||||
|
|
||||||
_general?.Dispose();
|
_general?.Dispose();
|
||||||
_general = null;
|
|
||||||
_layerBitmap?.Dispose();
|
_layerBitmap?.Dispose();
|
||||||
_layerBitmap = null;
|
|
||||||
_transform?.Dispose();
|
_transform?.Dispose();
|
||||||
_transform = null;
|
|
||||||
|
|
||||||
Profile = null;
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private void Initialize()
|
||||||
|
{
|
||||||
|
LayerBrushStore.LayerBrushAdded += LayerBrushStoreOnLayerBrushAdded;
|
||||||
|
LayerBrushStore.LayerBrushRemoved += LayerBrushStoreOnLayerBrushRemoved;
|
||||||
|
|
||||||
|
// Layers have two hardcoded property groups, instantiate them
|
||||||
|
var generalAttribute = Attribute.GetCustomAttribute(
|
||||||
|
GetType().GetProperty(nameof(General)),
|
||||||
|
typeof(PropertyGroupDescriptionAttribute)
|
||||||
|
);
|
||||||
|
var transformAttribute = Attribute.GetCustomAttribute(
|
||||||
|
GetType().GetProperty(nameof(Transform)),
|
||||||
|
typeof(PropertyGroupDescriptionAttribute)
|
||||||
|
);
|
||||||
|
General.GroupDescription = (PropertyGroupDescriptionAttribute) generalAttribute;
|
||||||
|
General.Initialize(this, "General.", Constants.CorePluginInfo);
|
||||||
|
Transform.GroupDescription = (PropertyGroupDescriptionAttribute) transformAttribute;
|
||||||
|
Transform.Initialize(this, "Transform.", Constants.CorePluginInfo);
|
||||||
|
|
||||||
|
General.ShapeType.BaseValueChanged += ShapeTypeOnBaseValueChanged;
|
||||||
|
ApplyShapeType();
|
||||||
|
ActivateLayerBrush();
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Storage
|
#region Storage
|
||||||
|
|
||||||
internal override void ApplyToEntity()
|
internal override void Load()
|
||||||
|
{
|
||||||
|
EntityId = LayerEntity.Id;
|
||||||
|
Name = LayerEntity.Name;
|
||||||
|
Enabled = LayerEntity.Enabled;
|
||||||
|
Order = LayerEntity.Order;
|
||||||
|
|
||||||
|
_expandedPropertyGroups.AddRange(LayerEntity.ExpandedPropertyGroups);
|
||||||
|
LoadRenderElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void Save()
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
throw new ObjectDisposedException("Layer");
|
throw new ObjectDisposedException("Layer");
|
||||||
@ -198,24 +206,13 @@ namespace Artemis.Core
|
|||||||
LayerEntity.Leds.Add(ledEntity);
|
LayerEntity.Leds.Add(ledEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Conditions
|
SaveRenderElement();
|
||||||
RenderElementEntity.RootDisplayCondition = DisplayConditionGroup?.Entity;
|
|
||||||
DisplayConditionGroup?.ApplyToEntity();
|
|
||||||
|
|
||||||
ApplyRenderElementToEntity();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Shape management
|
#region Shape management
|
||||||
|
|
||||||
private void GeneralOnPropertyGroupInitialized(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
ApplyShapeType();
|
|
||||||
General.ShapeType.BaseValueChanged -= ShapeTypeOnBaseValueChanged;
|
|
||||||
General.ShapeType.BaseValueChanged += ShapeTypeOnBaseValueChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ShapeTypeOnBaseValueChanged(object sender, EventArgs e)
|
private void ShapeTypeOnBaseValueChanged(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
ApplyShapeType();
|
ApplyShapeType();
|
||||||
@ -312,18 +309,6 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override List<BaseLayerProperty> GetAllLayerProperties()
|
|
||||||
{
|
|
||||||
var result = base.GetAllLayerProperties();
|
|
||||||
result.AddRange(General.GetAllLayerProperties());
|
|
||||||
result.AddRange(Transform.GetAllLayerProperties());
|
|
||||||
if (LayerBrush?.BaseProperties != null)
|
|
||||||
result.AddRange(LayerBrush.BaseProperties.GetAllLayerProperties());
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo)
|
public override void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo)
|
||||||
{
|
{
|
||||||
@ -383,7 +368,6 @@ namespace Artemis.Core
|
|||||||
if (Parent is Folder parentFolder)
|
if (Parent is Folder parentFolder)
|
||||||
targetLocation = Path.Bounds.Location - parentFolder.Path.Bounds.Location;
|
targetLocation = Path.Bounds.Location - parentFolder.Path.Bounds.Location;
|
||||||
|
|
||||||
|
|
||||||
canvas.DrawBitmap(_layerBitmap, targetLocation, layerPaint);
|
canvas.DrawBitmap(_layerBitmap, targetLocation, layerPaint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -680,7 +664,55 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Activation
|
#region Brush management
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the current layer brush to the brush described in the provided <paramref name="descriptor" />
|
||||||
|
/// </summary>
|
||||||
|
public void ChangeLayerBrush(LayerBrushDescriptor descriptor)
|
||||||
|
{
|
||||||
|
if (descriptor == null)
|
||||||
|
throw new ArgumentNullException(nameof(descriptor));
|
||||||
|
|
||||||
|
if (LayerBrush != null)
|
||||||
|
{
|
||||||
|
var brush = LayerBrush;
|
||||||
|
LayerBrush = null;
|
||||||
|
brush.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the brush reference matches the brush
|
||||||
|
var current = General.BrushReference.BaseValue;
|
||||||
|
if (!descriptor.MatchesLayerBrushReference(current))
|
||||||
|
General.BrushReference.BaseValue = new LayerBrushReference(descriptor);
|
||||||
|
|
||||||
|
ActivateLayerBrush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the current layer brush from the layer
|
||||||
|
/// </summary>
|
||||||
|
public void RemoveLayerBrush()
|
||||||
|
{
|
||||||
|
if (LayerBrush == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var brush = LayerBrush;
|
||||||
|
DeactivateLayerBrush();
|
||||||
|
LayerEntity.PropertyEntities.RemoveAll(p => p.PluginGuid == brush.PluginInfo.Guid && p.Path.StartsWith("LayerBrush."));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ActivateLayerBrush()
|
||||||
|
{
|
||||||
|
var current = General.BrushReference.CurrentValue;
|
||||||
|
if (current == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var descriptor = LayerBrushStore.Get(current.BrushPluginGuid, current.BrushType)?.LayerBrushDescriptor;
|
||||||
|
descriptor?.CreateInstance(this);
|
||||||
|
|
||||||
|
OnLayerBrushUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
internal void DeactivateLayerBrush()
|
internal void DeactivateLayerBrush()
|
||||||
{
|
{
|
||||||
@ -690,16 +722,29 @@ namespace Artemis.Core
|
|||||||
var brush = LayerBrush;
|
var brush = LayerBrush;
|
||||||
LayerBrush = null;
|
LayerBrush = null;
|
||||||
brush.Dispose();
|
brush.Dispose();
|
||||||
|
|
||||||
|
OnLayerBrushUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void RemoveLayerBrush()
|
#endregion
|
||||||
|
|
||||||
|
#region Event handlers
|
||||||
|
|
||||||
|
private void LayerBrushStoreOnLayerBrushRemoved(object sender, LayerBrushStoreEvent e)
|
||||||
{
|
{
|
||||||
if (LayerBrush == null)
|
if (LayerBrush?.Descriptor == e.Registration.LayerBrushDescriptor)
|
||||||
|
DeactivateLayerBrush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LayerBrushStoreOnLayerBrushAdded(object sender, LayerBrushStoreEvent e)
|
||||||
|
{
|
||||||
|
if (LayerBrush != null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var brush = LayerBrush;
|
var current = General.BrushReference.CurrentValue;
|
||||||
DeactivateLayerBrush();
|
if (e.Registration.Plugin.PluginInfo.Guid == current.BrushPluginGuid &&
|
||||||
LayerEntity.PropertyEntities.RemoveAll(p => p.PluginGuid == brush.PluginInfo.Guid && p.Path.StartsWith("LayerBrush."));
|
e.Registration.LayerBrushDescriptor.LayerBrushType.Name == current.BrushType)
|
||||||
|
ActivateLayerBrush();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@ -17,5 +17,15 @@ namespace Artemis.Core
|
|||||||
/// The full type name of the brush descriptor
|
/// The full type name of the brush descriptor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string BrushType { get; set; }
|
public string BrushType { get; set; }
|
||||||
|
|
||||||
|
public LayerBrushReference()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public LayerBrushReference(LayerBrushDescriptor descriptor)
|
||||||
|
{
|
||||||
|
BrushPluginGuid = descriptor.LayerBrushProvider.PluginInfo.Guid;
|
||||||
|
BrushType = descriptor.LayerBrushType.Name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,273 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
|
||||||
using Artemis.Core.Services;
|
|
||||||
using Artemis.Storage.Entities.Profile;
|
|
||||||
|
|
||||||
namespace Artemis.Core
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// For internal use only, to implement your own layer property type, extend <see cref="LayerProperty{T}" /> instead.
|
|
||||||
/// </summary>
|
|
||||||
public abstract class BaseLayerProperty
|
|
||||||
{
|
|
||||||
protected readonly List<DataBindingRegistration> _dataBindingRegistrations = new List<DataBindingRegistration>();
|
|
||||||
protected readonly List<DataBinding> _dataBindings = new List<DataBinding>();
|
|
||||||
|
|
||||||
private object _baseValue;
|
|
||||||
private object _currentValue;
|
|
||||||
private object _defaultValue;
|
|
||||||
private bool _isHidden;
|
|
||||||
private bool _keyframesEnabled;
|
|
||||||
|
|
||||||
internal BaseLayerProperty()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the base value of this layer property without any keyframes applied
|
|
||||||
/// </summary>
|
|
||||||
public object BaseValue
|
|
||||||
{
|
|
||||||
get => _baseValue;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value != null && value.GetType() != GetPropertyType())
|
|
||||||
throw new ArtemisCoreException("Cannot update base value because of a type mismatch");
|
|
||||||
_baseValue = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current value of this property as it is affected by it's keyframes, updated once every frame
|
|
||||||
/// </summary>
|
|
||||||
public object CurrentValue
|
|
||||||
{
|
|
||||||
get => _currentValue;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value != null && value.GetType() != GetPropertyType())
|
|
||||||
throw new ArtemisCoreException("Cannot update current value because of a type mismatch");
|
|
||||||
_currentValue = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the default value of this layer property. If set, this value is automatically applied if the property
|
|
||||||
/// has no value in storage
|
|
||||||
/// </summary>
|
|
||||||
public object DefaultValue
|
|
||||||
{
|
|
||||||
get => _defaultValue;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value != null && value.GetType() != GetPropertyType())
|
|
||||||
throw new ArtemisCoreException("Cannot update default value because of a type mismatch");
|
|
||||||
_defaultValue = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a list containing the active data bindings
|
|
||||||
/// </summary>
|
|
||||||
public IReadOnlyList<DataBinding> DataBindings => _dataBindings.AsReadOnly();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a list containing all the data binding registrations
|
|
||||||
/// </summary>
|
|
||||||
public IReadOnlyList<DataBindingRegistration> DataBindingRegistrations => _dataBindingRegistrations.AsReadOnly();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the profile element (such as layer or folder) this effect is applied to
|
|
||||||
/// </summary>
|
|
||||||
public RenderProfileElement ProfileElement { get; internal set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The parent group of this layer property, set after construction
|
|
||||||
/// </summary>
|
|
||||||
public LayerPropertyGroup Parent { get; internal set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets whether keyframes are supported on this type of property
|
|
||||||
/// </summary>
|
|
||||||
public bool KeyframesSupported { get; protected internal set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets whether data bindings are supported on this type of property
|
|
||||||
/// </summary>
|
|
||||||
public bool DataBindingsSupported { get; protected internal set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets whether keyframes are enabled on this property, has no effect if <see cref="KeyframesSupported" /> is
|
|
||||||
/// False
|
|
||||||
/// </summary>
|
|
||||||
public bool KeyframesEnabled
|
|
||||||
{
|
|
||||||
get => _keyframesEnabled;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_keyframesEnabled == value) return;
|
|
||||||
_keyframesEnabled = value;
|
|
||||||
OnKeyframesToggled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets whether the property is hidden in the UI
|
|
||||||
/// </summary>
|
|
||||||
public bool IsHidden
|
|
||||||
{
|
|
||||||
get => _isHidden;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_isHidden = value;
|
|
||||||
OnVisibilityChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates whether the BaseValue was loaded from storage, useful to check whether a default value must be applied
|
|
||||||
/// </summary>
|
|
||||||
public bool IsLoadedFromStorage { get; internal set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Used to declare that this property doesn't belong to a plugin and should use the core plugin GUID
|
|
||||||
/// </summary>
|
|
||||||
public bool IsCoreProperty { get; internal set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the description attribute applied to this property
|
|
||||||
/// </summary>
|
|
||||||
public PropertyDescriptionAttribute PropertyDescription { get; internal set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a list of all the keyframes in their non-generic base form, without their values being available
|
|
||||||
/// </summary>
|
|
||||||
public abstract IReadOnlyList<BaseLayerPropertyKeyframe> BaseKeyframes { get; }
|
|
||||||
|
|
||||||
internal PropertyEntity PropertyEntity { get; set; }
|
|
||||||
internal LayerPropertyGroup LayerPropertyGroup { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Overrides the property value with the default value
|
|
||||||
/// </summary>
|
|
||||||
public abstract void ApplyDefaultValue();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the type of the property
|
|
||||||
/// </summary>
|
|
||||||
public abstract Type GetPropertyType();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Applies the provided property entity to the layer property by deserializing the JSON base value and keyframe values
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="entity"></param>
|
|
||||||
/// <param name="layerPropertyGroup"></param>
|
|
||||||
/// <param name="fromStorage"></param>
|
|
||||||
internal abstract void ApplyToLayerProperty(PropertyEntity entity, LayerPropertyGroup layerPropertyGroup, bool fromStorage);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Saves the property to the underlying property entity that was configured when calling
|
|
||||||
/// <see cref="ApplyToLayerProperty" />
|
|
||||||
/// </summary>
|
|
||||||
internal abstract void ApplyToEntity();
|
|
||||||
|
|
||||||
#region Data bindings
|
|
||||||
|
|
||||||
internal void InitializeDataBindings(IDataModelService dataModelService, IDataBindingService dataBindingService)
|
|
||||||
{
|
|
||||||
foreach (var dataBinding in DataBindings)
|
|
||||||
dataBinding.Initialize(dataModelService, dataBindingService);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a new data binding targeting the given property to the <see cref="DataBindings" /> collection
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The newly created data binding</returns>
|
|
||||||
public DataBinding EnableDataBinding(DataBindingRegistration dataBindingRegistration)
|
|
||||||
{
|
|
||||||
if (dataBindingRegistration.LayerProperty != this)
|
|
||||||
throw new ArtemisCoreException("Cannot enable a data binding using a data binding registration of a different layer property");
|
|
||||||
|
|
||||||
var dataBinding = new DataBinding(dataBindingRegistration);
|
|
||||||
_dataBindings.Add(dataBinding);
|
|
||||||
|
|
||||||
return dataBinding;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes the provided data binding from the <see cref="DataBindings" /> collection
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dataBinding">The data binding to remove</param>
|
|
||||||
public void DisableDataBinding(DataBinding dataBinding)
|
|
||||||
{
|
|
||||||
_dataBindings.Remove(dataBinding);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Events
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs once every frame when the layer property is updated
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<LayerPropertyEventArgs> Updated;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when the base value of the layer property was updated
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<LayerPropertyEventArgs> BaseValueChanged;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when the <see cref="IsHidden" /> value of the layer property was updated
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<LayerPropertyEventArgs> VisibilityChanged;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when keyframes are enabled/disabled
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<LayerPropertyEventArgs> KeyframesToggled;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when a new keyframe was added to the layer property
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<LayerPropertyEventArgs> KeyframeAdded;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when a keyframe was removed from the layer property
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<LayerPropertyEventArgs> KeyframeRemoved;
|
|
||||||
|
|
||||||
protected virtual void OnUpdated()
|
|
||||||
{
|
|
||||||
Updated?.Invoke(this, new LayerPropertyEventArgs(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnBaseValueChanged()
|
|
||||||
{
|
|
||||||
BaseValueChanged?.Invoke(this, new LayerPropertyEventArgs(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnVisibilityChanged()
|
|
||||||
{
|
|
||||||
VisibilityChanged?.Invoke(this, new LayerPropertyEventArgs(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnKeyframesToggled()
|
|
||||||
{
|
|
||||||
KeyframesToggled?.Invoke(this, new LayerPropertyEventArgs(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnKeyframeAdded()
|
|
||||||
{
|
|
||||||
KeyframeAdded?.Invoke(this, new LayerPropertyEventArgs(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnKeyframeRemoved()
|
|
||||||
{
|
|
||||||
KeyframeRemoved?.Invoke(this, new LayerPropertyEventArgs(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Stylet;
|
|
||||||
|
|
||||||
namespace Artemis.Core
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// For internal use only, use <see cref="LayerPropertyKeyframe{T}" /> instead.
|
|
||||||
/// </summary>
|
|
||||||
public abstract class BaseLayerPropertyKeyframe : PropertyChangedBase
|
|
||||||
{
|
|
||||||
internal BaseLayerPropertyKeyframe(BaseLayerProperty baseLayerProperty)
|
|
||||||
{
|
|
||||||
BaseLayerProperty = baseLayerProperty;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The base class of the layer property this keyframe is applied to
|
|
||||||
/// </summary>
|
|
||||||
public BaseLayerProperty BaseLayerProperty { get; internal set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The position of this keyframe in the timeline
|
|
||||||
/// </summary>
|
|
||||||
public abstract TimeSpan Position { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The easing function applied on the value of the keyframe
|
|
||||||
/// </summary>
|
|
||||||
public Easings.Functions EasingFunction { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes the keyframe from the layer property
|
|
||||||
/// </summary>
|
|
||||||
public abstract void Remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Artemis.Storage.Entities.Profile;
|
||||||
|
|
||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a property on a layer. Properties are saved in storage and can optionally be modified from the UI.
|
||||||
|
/// <para>
|
||||||
|
/// Note: You cannot initialize layer properties yourself. If properly placed and annotated, the Artemis core will
|
||||||
|
/// initialize these for you.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
public interface ILayerProperty : IStorageModel, IUpdateModel, IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the layer property
|
||||||
|
/// <para>
|
||||||
|
/// Note: This isn't done in the constructor to keep it parameterless which is easier for implementations of
|
||||||
|
/// <see cref="LayerProperty{T}" />
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
void Initialize(RenderProfileElement profileElement, LayerPropertyGroup group, PropertyEntity entity, bool fromStorage, PropertyDescriptionAttribute description);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a list off all data binding registrations
|
||||||
|
/// </summary>
|
||||||
|
List<IDataBindingRegistration> GetAllDataBindingRegistrations();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
@ -17,52 +16,191 @@ namespace Artemis.Core
|
|||||||
/// </para>
|
/// </para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of property encapsulated in this layer property</typeparam>
|
/// <typeparam name="T">The type of property encapsulated in this layer property</typeparam>
|
||||||
public abstract class LayerProperty<T> : BaseLayerProperty
|
public abstract class LayerProperty<T> : ILayerProperty
|
||||||
{
|
{
|
||||||
private bool _isInitialized;
|
private bool _disposed;
|
||||||
private List<LayerPropertyKeyframe<T>> _keyframes;
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="LayerProperty{T}" /> class
|
||||||
|
/// </summary>
|
||||||
protected LayerProperty()
|
protected LayerProperty()
|
||||||
{
|
{
|
||||||
_keyframes = new List<LayerPropertyKeyframe<T>>();
|
_keyframes = new List<LayerPropertyKeyframe<T>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the base value of this layer property without any keyframes applied
|
/// Gets the description attribute applied to this property
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public new T BaseValue
|
public PropertyDescriptionAttribute PropertyDescription { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the property, applying keyframes and data bindings to the current value
|
||||||
|
/// </summary>
|
||||||
|
public void Update(double deltaTime)
|
||||||
{
|
{
|
||||||
get => base.BaseValue != null ? (T) base.BaseValue : default;
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("LayerProperty");
|
||||||
|
|
||||||
|
CurrentValue = BaseValue;
|
||||||
|
|
||||||
|
UpdateKeyframes();
|
||||||
|
UpdateDataBindings(deltaTime);
|
||||||
|
|
||||||
|
OnUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the type of the property
|
||||||
|
/// </summary>
|
||||||
|
public Type GetPropertyType()
|
||||||
|
{
|
||||||
|
return typeof(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Hierarchy
|
||||||
|
|
||||||
|
private bool _isHidden;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether the property is hidden in the UI
|
||||||
|
/// </summary>
|
||||||
|
public bool IsHidden
|
||||||
|
{
|
||||||
|
get => _isHidden;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (Equals(base.BaseValue, value))
|
_isHidden = value;
|
||||||
|
OnVisibilityChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the profile element (such as layer or folder) this property is applied to
|
||||||
|
/// </summary>
|
||||||
|
public RenderProfileElement ProfileElement { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The parent group of this layer property, set after construction
|
||||||
|
/// </summary>
|
||||||
|
public LayerPropertyGroup LayerPropertyGroup { get; internal set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Value management
|
||||||
|
|
||||||
|
private T _baseValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called every update (if keyframes are both supported and enabled) to determine the new <see cref="CurrentValue" />
|
||||||
|
/// based on the provided progress
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="keyframeProgress">The linear current keyframe progress</param>
|
||||||
|
/// <param name="keyframeProgressEased">The current keyframe progress, eased with the current easing function</param>
|
||||||
|
protected virtual void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the base value of this layer property without any keyframes applied
|
||||||
|
/// </summary>
|
||||||
|
public T BaseValue
|
||||||
|
{
|
||||||
|
get => _baseValue;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (Equals(_baseValue, value))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
base.BaseValue = value;
|
_baseValue = value;
|
||||||
Update();
|
Update(0);
|
||||||
OnBaseValueChanged();
|
OnBaseValueChanged();
|
||||||
|
LayerPropertyGroup.OnLayerPropertyBaseValueChanged(new LayerPropertyEventArgs(this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the current value of this property as it is affected by it's keyframes, updated once every frame
|
/// Gets the current value of this property as it is affected by it's keyframes, updated once every frame
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public new T CurrentValue
|
public T CurrentValue { get; set; }
|
||||||
{
|
|
||||||
get => base.CurrentValue != null ? (T) base.CurrentValue : default;
|
|
||||||
set => base.CurrentValue = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the default value of this layer property. If set, this value is automatically applied if the property
|
/// Gets or sets the default value of this layer property. If set, this value is automatically applied if the property
|
||||||
/// has no value in storage
|
/// has no value in storage
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public new T DefaultValue
|
public T DefaultValue { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the current value, using either keyframes if enabled or the base value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to set.</param>
|
||||||
|
/// <param name="time">
|
||||||
|
/// An optional time to set the value add, if provided and property is using keyframes the value will be set to an new
|
||||||
|
/// or existing keyframe.
|
||||||
|
/// </param>
|
||||||
|
public void SetCurrentValue(T value, TimeSpan? time)
|
||||||
{
|
{
|
||||||
get => base.DefaultValue != null ? (T) base.DefaultValue : default;
|
if (_disposed)
|
||||||
set => base.DefaultValue = value;
|
throw new ObjectDisposedException("LayerProperty");
|
||||||
|
|
||||||
|
if (time == null || !KeyframesEnabled || !KeyframesSupported)
|
||||||
|
BaseValue = value;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If on a keyframe, update the keyframe
|
||||||
|
var currentKeyframe = Keyframes.FirstOrDefault(k => k.Position == time.Value);
|
||||||
|
// Create a new keyframe if none found
|
||||||
|
if (currentKeyframe == null)
|
||||||
|
AddKeyframe(new LayerPropertyKeyframe<T>(value, time.Value, Easings.Functions.Linear, this));
|
||||||
|
else
|
||||||
|
currentKeyframe.Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force an update so that the base value is applied to the current value and
|
||||||
|
// keyframes/data bindings are applied using the new base value
|
||||||
|
Update(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overrides the property value with the default value
|
||||||
|
/// </summary>
|
||||||
|
public void ApplyDefaultValue()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("LayerProperty");
|
||||||
|
|
||||||
|
BaseValue = DefaultValue;
|
||||||
|
CurrentValue = DefaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Keyframes
|
||||||
|
|
||||||
|
private bool _keyframesEnabled;
|
||||||
|
private List<LayerPropertyKeyframe<T>> _keyframes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets whether keyframes are supported on this type of property
|
||||||
|
/// </summary>
|
||||||
|
public bool KeyframesSupported { get; protected internal set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether keyframes are enabled on this property, has no effect if <see cref="KeyframesSupported" /> is
|
||||||
|
/// False
|
||||||
|
/// </summary>
|
||||||
|
public bool KeyframesEnabled
|
||||||
|
{
|
||||||
|
get => _keyframesEnabled;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_keyframesEnabled == value) return;
|
||||||
|
_keyframesEnabled = value;
|
||||||
|
OnKeyframesToggled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a read-only list of all the keyframes on this layer property
|
/// Gets a read-only list of all the keyframes on this layer property
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -78,50 +216,22 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public LayerPropertyKeyframe<T> NextKeyframe { get; protected set; }
|
public LayerPropertyKeyframe<T> NextKeyframe { get; protected set; }
|
||||||
|
|
||||||
public override IReadOnlyList<BaseLayerPropertyKeyframe> BaseKeyframes => _keyframes.Cast<BaseLayerPropertyKeyframe>().ToList().AsReadOnly();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the current value, using either keyframes if enabled or the base value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The value to set.</param>
|
|
||||||
/// <param name="time">
|
|
||||||
/// An optional time to set the value add, if provided and property is using keyframes the value will be set to an new
|
|
||||||
/// or existing keyframe.
|
|
||||||
/// </param>
|
|
||||||
public void SetCurrentValue(T value, TimeSpan? time)
|
|
||||||
{
|
|
||||||
if (time == null || !KeyframesEnabled || !KeyframesSupported)
|
|
||||||
BaseValue = value;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// If on a keyframe, update the keyframe
|
|
||||||
var currentKeyframe = Keyframes.FirstOrDefault(k => k.Position == time.Value);
|
|
||||||
// Create a new keyframe if none found
|
|
||||||
if (currentKeyframe == null)
|
|
||||||
AddKeyframe(new LayerPropertyKeyframe<T>(value, time.Value, Easings.Functions.Linear, this));
|
|
||||||
else
|
|
||||||
currentKeyframe.Value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Force an update so that the base value is applied to the current value and
|
|
||||||
// keyframes/data bindings are applied using the new base value
|
|
||||||
Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a keyframe to the layer property
|
/// Adds a keyframe to the layer property
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="keyframe">The keyframe to add</param>
|
/// <param name="keyframe">The keyframe to add</param>
|
||||||
public void AddKeyframe(LayerPropertyKeyframe<T> keyframe)
|
public void AddKeyframe(LayerPropertyKeyframe<T> keyframe)
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("LayerProperty");
|
||||||
|
|
||||||
if (_keyframes.Contains(keyframe))
|
if (_keyframes.Contains(keyframe))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
keyframe.LayerProperty?.RemoveKeyframe(keyframe);
|
keyframe.LayerProperty?.RemoveKeyframe(keyframe);
|
||||||
|
|
||||||
keyframe.LayerProperty = this;
|
keyframe.LayerProperty = this;
|
||||||
keyframe.BaseLayerProperty = this;
|
|
||||||
_keyframes.Add(keyframe);
|
_keyframes.Add(keyframe);
|
||||||
|
|
||||||
SortKeyframes();
|
SortKeyframes();
|
||||||
OnKeyframeAdded();
|
OnKeyframeAdded();
|
||||||
}
|
}
|
||||||
@ -132,6 +242,9 @@ namespace Artemis.Core
|
|||||||
/// <param name="keyframe">The keyframe to remove</param>
|
/// <param name="keyframe">The keyframe to remove</param>
|
||||||
public LayerPropertyKeyframe<T> CopyKeyframe(LayerPropertyKeyframe<T> keyframe)
|
public LayerPropertyKeyframe<T> CopyKeyframe(LayerPropertyKeyframe<T> keyframe)
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("LayerProperty");
|
||||||
|
|
||||||
var newKeyframe = new LayerPropertyKeyframe<T>(
|
var newKeyframe = new LayerPropertyKeyframe<T>(
|
||||||
keyframe.Value,
|
keyframe.Value,
|
||||||
keyframe.Position,
|
keyframe.Position,
|
||||||
@ -149,63 +262,18 @@ namespace Artemis.Core
|
|||||||
/// <param name="keyframe">The keyframe to remove</param>
|
/// <param name="keyframe">The keyframe to remove</param>
|
||||||
public void RemoveKeyframe(LayerPropertyKeyframe<T> keyframe)
|
public void RemoveKeyframe(LayerPropertyKeyframe<T> keyframe)
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("LayerProperty");
|
||||||
|
|
||||||
if (!_keyframes.Contains(keyframe))
|
if (!_keyframes.Contains(keyframe))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_keyframes.Remove(keyframe);
|
_keyframes.Remove(keyframe);
|
||||||
keyframe.LayerProperty = null;
|
keyframe.LayerProperty = null;
|
||||||
keyframe.BaseLayerProperty = null;
|
|
||||||
SortKeyframes();
|
SortKeyframes();
|
||||||
OnKeyframeRemoved();
|
OnKeyframeRemoved();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes all keyframes from the layer property
|
|
||||||
/// </summary>
|
|
||||||
public void ClearKeyframes()
|
|
||||||
{
|
|
||||||
var keyframes = new List<LayerPropertyKeyframe<T>>(_keyframes);
|
|
||||||
foreach (var layerPropertyKeyframe in keyframes)
|
|
||||||
RemoveKeyframe(layerPropertyKeyframe);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void ApplyDefaultValue()
|
|
||||||
{
|
|
||||||
BaseValue = DefaultValue;
|
|
||||||
CurrentValue = DefaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override Type GetPropertyType()
|
|
||||||
{
|
|
||||||
return typeof(T);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called every update (if keyframes are both supported and enabled) to determine the new <see cref="CurrentValue" />
|
|
||||||
/// based on the provided progress
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="keyframeProgress">The linear current keyframe progress</param>
|
|
||||||
/// <param name="keyframeProgressEased">The current keyframe progress, eased with the current easing function</param>
|
|
||||||
protected virtual void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Updates the property, applying keyframes and data bindings to the current value
|
|
||||||
/// </summary>
|
|
||||||
internal void Update(double deltaTime = 0)
|
|
||||||
{
|
|
||||||
CurrentValue = BaseValue;
|
|
||||||
|
|
||||||
UpdateKeyframes();
|
|
||||||
UpdateDataBindings(deltaTime);
|
|
||||||
|
|
||||||
OnUpdated();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sorts the keyframes in ascending order by position
|
/// Sorts the keyframes in ascending order by position
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -214,77 +282,6 @@ namespace Artemis.Core
|
|||||||
_keyframes = _keyframes.OrderBy(k => k.Position).ToList();
|
_keyframes = _keyframes.OrderBy(k => k.Position).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal override void ApplyToLayerProperty(PropertyEntity entity, LayerPropertyGroup layerPropertyGroup, bool fromStorage)
|
|
||||||
{
|
|
||||||
// Doubt this will happen but let's make sure
|
|
||||||
if (_isInitialized)
|
|
||||||
throw new ArtemisCoreException("Layer property already initialized, wut");
|
|
||||||
|
|
||||||
PropertyEntity = entity;
|
|
||||||
LayerPropertyGroup = layerPropertyGroup;
|
|
||||||
LayerPropertyGroup.PropertyGroupUpdating += (sender, args) => Update(args.DeltaTime);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (entity.Value != null)
|
|
||||||
BaseValue = JsonConvert.DeserializeObject<T>(entity.Value);
|
|
||||||
|
|
||||||
IsLoadedFromStorage = fromStorage;
|
|
||||||
CurrentValue = BaseValue;
|
|
||||||
KeyframesEnabled = entity.KeyframesEnabled;
|
|
||||||
|
|
||||||
_keyframes.Clear();
|
|
||||||
_keyframes.AddRange(entity.KeyframeEntities.Select(k => new LayerPropertyKeyframe<T>(
|
|
||||||
JsonConvert.DeserializeObject<T>(k.Value),
|
|
||||||
k.Position,
|
|
||||||
(Easings.Functions) k.EasingFunction,
|
|
||||||
this
|
|
||||||
)));
|
|
||||||
|
|
||||||
_dataBindings.Clear();
|
|
||||||
foreach (var entityDataBindingEntity in entity.DataBindingEntities)
|
|
||||||
{
|
|
||||||
var dataBinding = new DataBinding(this, entityDataBindingEntity);
|
|
||||||
_dataBindings.Add(dataBinding);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (JsonException e)
|
|
||||||
{
|
|
||||||
// TODO: Properly log the JSON exception
|
|
||||||
Debug.WriteLine($"JSON exception while deserializing: {e}");
|
|
||||||
IsLoadedFromStorage = false;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
SortKeyframes();
|
|
||||||
_isInitialized = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void ApplyToEntity()
|
|
||||||
{
|
|
||||||
if (!_isInitialized)
|
|
||||||
throw new ArtemisCoreException("Layer property is not yet initialized");
|
|
||||||
|
|
||||||
PropertyEntity.Value = JsonConvert.SerializeObject(BaseValue);
|
|
||||||
PropertyEntity.KeyframesEnabled = KeyframesEnabled;
|
|
||||||
PropertyEntity.KeyframeEntities.Clear();
|
|
||||||
PropertyEntity.KeyframeEntities.AddRange(Keyframes.Select(k => new KeyframeEntity
|
|
||||||
{
|
|
||||||
Value = JsonConvert.SerializeObject(k.Value),
|
|
||||||
Position = k.Position,
|
|
||||||
EasingFunction = (int) k.EasingFunction
|
|
||||||
}));
|
|
||||||
|
|
||||||
PropertyEntity.DataBindingEntities.Clear();
|
|
||||||
foreach (var dataBinding in DataBindings)
|
|
||||||
{
|
|
||||||
dataBinding.ApplyToEntity();
|
|
||||||
PropertyEntity.DataBindingEntities.Add(dataBinding.Entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateKeyframes()
|
private void UpdateKeyframes()
|
||||||
{
|
{
|
||||||
if (!KeyframesSupported || !KeyframesEnabled)
|
if (!KeyframesSupported || !KeyframesEnabled)
|
||||||
@ -311,48 +308,269 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Data bindings
|
#region Data bindings
|
||||||
|
|
||||||
public void RegisterDataBindingProperty<TProperty>(Expression<Func<T, TProperty>> propertyLambda, DataBindingConverter converter)
|
internal readonly List<IDataBindingRegistration> _dataBindingRegistrations = new List<IDataBindingRegistration>();
|
||||||
{
|
internal readonly List<IDataBinding> _dataBindings = new List<IDataBinding>();
|
||||||
// If the lambda references to itself, use the property info of public new T CurrentValue
|
|
||||||
PropertyInfo propertyInfo;
|
|
||||||
string path = null;
|
|
||||||
if (propertyLambda.Parameters[0] == propertyLambda.Body)
|
|
||||||
{
|
|
||||||
propertyInfo = GetType().GetProperties().FirstOrDefault(p => p.Name == nameof(CurrentValue) && p.PropertyType == typeof(T));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
propertyInfo = ReflectionUtilities.GetPropertyInfo(CurrentValue, propertyLambda);
|
|
||||||
// Deconstruct the lambda
|
|
||||||
var current = (MemberExpression) propertyLambda.Body;
|
|
||||||
path = current.Member.Name;
|
|
||||||
while (current.Expression is MemberExpression memberExpression)
|
|
||||||
{
|
|
||||||
path = current.Member.Name + "." + path;
|
|
||||||
current = memberExpression;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (converter.SupportedType != propertyInfo.PropertyType)
|
/// <summary>
|
||||||
|
/// Gets whether data bindings are supported on this type of property
|
||||||
|
/// </summary>
|
||||||
|
public bool DataBindingsSupported { get; protected internal set; } = true;
|
||||||
|
|
||||||
|
public DataBindingRegistration<T, TProperty> GetDataBindingRegistration<TProperty>(string expression)
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("LayerProperty");
|
||||||
|
|
||||||
|
var match = _dataBindingRegistrations.FirstOrDefault(r => r is DataBindingRegistration<T, TProperty> registration &&
|
||||||
|
registration.PropertyExpression.ToString() == expression);
|
||||||
|
return (DataBindingRegistration<T, TProperty>) match;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<IDataBindingRegistration> GetAllDataBindingRegistrations()
|
||||||
|
{
|
||||||
|
return _dataBindingRegistrations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterDataBindingProperty<TProperty>(Expression<Func<T, TProperty>> propertyExpression, DataBindingConverter<T, TProperty> converter)
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("LayerProperty");
|
||||||
|
|
||||||
|
if (propertyExpression.Body.NodeType != ExpressionType.MemberAccess && propertyExpression.Body.NodeType != ExpressionType.Parameter)
|
||||||
|
throw new ArtemisCoreException("Provided expression is invalid, it must be 'value => value' or 'value => value.Property'");
|
||||||
|
|
||||||
|
if (converter.SupportedType != propertyExpression.ReturnType)
|
||||||
{
|
{
|
||||||
throw new ArtemisCoreException($"Cannot register data binding property for property {propertyInfo.Name} " +
|
throw new ArtemisCoreException($"Cannot register data binding property for property {PropertyDescription.Name} " +
|
||||||
"because the provided converter does not support the property's type");
|
"because the provided converter does not support the property's type");
|
||||||
}
|
}
|
||||||
|
|
||||||
_dataBindingRegistrations.Add(new DataBindingRegistration(this, propertyInfo, converter, path));
|
_dataBindingRegistrations.Add(new DataBindingRegistration<T, TProperty>(this, converter, propertyExpression));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enables a data binding for the provided <paramref name="dataBindingRegistration" />
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The newly created data binding</returns>
|
||||||
|
public DataBinding<T, TProperty> EnableDataBinding<TProperty>(DataBindingRegistration<T, TProperty> dataBindingRegistration)
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("LayerProperty");
|
||||||
|
|
||||||
|
if (dataBindingRegistration.LayerProperty != this)
|
||||||
|
throw new ArtemisCoreException("Cannot enable a data binding using a data binding registration of a different layer property");
|
||||||
|
|
||||||
|
var dataBinding = new DataBinding<T, TProperty>(dataBindingRegistration);
|
||||||
|
_dataBindings.Add(dataBinding);
|
||||||
|
|
||||||
|
return dataBinding;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disables the provided data binding
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dataBinding">The data binding to remove</param>
|
||||||
|
public void DisableDataBinding<TProperty>(DataBinding<T, TProperty> dataBinding)
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("LayerProperty");
|
||||||
|
|
||||||
|
_dataBindings.Remove(dataBinding);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateDataBindings(double deltaTime)
|
private void UpdateDataBindings(double deltaTime)
|
||||||
{
|
{
|
||||||
foreach (var dataBinding in DataBindings)
|
foreach (var dataBinding in _dataBindings)
|
||||||
{
|
{
|
||||||
dataBinding.Update(deltaTime);
|
dataBinding.Update(deltaTime);
|
||||||
dataBinding.ApplyToProperty();
|
dataBinding.Apply();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Storage
|
||||||
|
|
||||||
|
private bool _isInitialized;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the BaseValue was loaded from storage, useful to check whether a default value must be applied
|
||||||
|
/// </summary>
|
||||||
|
public bool IsLoadedFromStorage { get; internal set; }
|
||||||
|
|
||||||
|
internal PropertyEntity Entity { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Initialize(RenderProfileElement profileElement, LayerPropertyGroup group, PropertyEntity entity, bool fromStorage, PropertyDescriptionAttribute description)
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("LayerProperty");
|
||||||
|
|
||||||
|
_isInitialized = true;
|
||||||
|
|
||||||
|
ProfileElement = profileElement ?? throw new ArgumentNullException(nameof(profileElement));
|
||||||
|
LayerPropertyGroup = group ?? throw new ArgumentNullException(nameof(group));
|
||||||
|
Entity = entity ?? throw new ArgumentNullException(nameof(entity));
|
||||||
|
PropertyDescription = description ?? throw new ArgumentNullException(nameof(description));
|
||||||
|
IsLoadedFromStorage = fromStorage;
|
||||||
|
LayerPropertyGroup.PropertyGroupUpdating += (sender, args) => Update(args.DeltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Load()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("LayerProperty");
|
||||||
|
|
||||||
|
if (!_isInitialized)
|
||||||
|
throw new ArtemisCoreException("Layer property is not yet initialized");
|
||||||
|
|
||||||
|
if (!IsLoadedFromStorage)
|
||||||
|
ApplyDefaultValue();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Entity.Value != null)
|
||||||
|
BaseValue = JsonConvert.DeserializeObject<T>(Entity.Value);
|
||||||
|
}
|
||||||
|
catch (JsonException e)
|
||||||
|
{
|
||||||
|
// ignored for now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentValue = BaseValue;
|
||||||
|
KeyframesEnabled = Entity.KeyframesEnabled;
|
||||||
|
|
||||||
|
_keyframes.Clear();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_keyframes.AddRange(Entity.KeyframeEntities.Select(k => new LayerPropertyKeyframe<T>(
|
||||||
|
JsonConvert.DeserializeObject<T>(k.Value),
|
||||||
|
k.Position,
|
||||||
|
(Easings.Functions) k.EasingFunction,
|
||||||
|
this
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
catch (JsonException e)
|
||||||
|
{
|
||||||
|
// ignored for now
|
||||||
|
}
|
||||||
|
|
||||||
|
_dataBindings.Clear();
|
||||||
|
foreach (var dataBindingRegistration in _dataBindingRegistrations)
|
||||||
|
{
|
||||||
|
var dataBinding = dataBindingRegistration.CreateDataBinding();
|
||||||
|
if (dataBinding != null)
|
||||||
|
_dataBindings.Add(dataBinding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Saves the property to the underlying property entity that was configured when calling
|
||||||
|
/// <see cref="ApplyToLayerProperty" />
|
||||||
|
/// </summary>
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("LayerProperty");
|
||||||
|
|
||||||
|
if (!_isInitialized)
|
||||||
|
throw new ArtemisCoreException("Layer property is not yet initialized");
|
||||||
|
|
||||||
|
Entity.Value = JsonConvert.SerializeObject(BaseValue);
|
||||||
|
Entity.KeyframesEnabled = KeyframesEnabled;
|
||||||
|
Entity.KeyframeEntities.Clear();
|
||||||
|
Entity.KeyframeEntities.AddRange(Keyframes.Select(k => new KeyframeEntity
|
||||||
|
{
|
||||||
|
Value = JsonConvert.SerializeObject(k.Value),
|
||||||
|
Position = k.Position,
|
||||||
|
EasingFunction = (int) k.EasingFunction
|
||||||
|
}));
|
||||||
|
|
||||||
|
Entity.DataBindingEntities.Clear();
|
||||||
|
foreach (var dataBinding in _dataBindings)
|
||||||
|
dataBinding.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Events
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs once every frame when the layer property is updated
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<LayerPropertyEventArgs<T>> Updated;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the base value of the layer property was updated
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<LayerPropertyEventArgs<T>> BaseValueChanged;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the <see cref="IsHidden" /> value of the layer property was updated
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<LayerPropertyEventArgs<T>> VisibilityChanged;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when keyframes are enabled/disabled
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<LayerPropertyEventArgs<T>> KeyframesToggled;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when a new keyframe was added to the layer property
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<LayerPropertyEventArgs<T>> KeyframeAdded;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when a keyframe was removed from the layer property
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<LayerPropertyEventArgs<T>> KeyframeRemoved;
|
||||||
|
|
||||||
|
protected virtual void OnUpdated()
|
||||||
|
{
|
||||||
|
Updated?.Invoke(this, new LayerPropertyEventArgs<T>(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnBaseValueChanged()
|
||||||
|
{
|
||||||
|
BaseValueChanged?.Invoke(this, new LayerPropertyEventArgs<T>(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnVisibilityChanged()
|
||||||
|
{
|
||||||
|
VisibilityChanged?.Invoke(this, new LayerPropertyEventArgs<T>(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnKeyframesToggled()
|
||||||
|
{
|
||||||
|
KeyframesToggled?.Invoke(this, new LayerPropertyEventArgs<T>(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnKeyframeAdded()
|
||||||
|
{
|
||||||
|
KeyframeAdded?.Invoke(this, new LayerPropertyEventArgs<T>(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnKeyframeRemoved()
|
||||||
|
{
|
||||||
|
KeyframeRemoved?.Invoke(this, new LayerPropertyEventArgs<T>(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_disposed = true;
|
||||||
|
|
||||||
|
foreach (var dataBinding in _dataBindings)
|
||||||
|
dataBinding.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,16 +1,28 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Stylet;
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
public class LayerPropertyKeyframe<T> : BaseLayerPropertyKeyframe
|
/// <summary>
|
||||||
|
/// Represents a keyframe on a <see cref="LayerProperty{T}" /> containing a value and a timestamp
|
||||||
|
/// </summary>
|
||||||
|
public class LayerPropertyKeyframe<T> : PropertyChangedBase
|
||||||
{
|
{
|
||||||
private LayerProperty<T> _layerProperty;
|
private LayerProperty<T> _layerProperty;
|
||||||
private TimeSpan _position;
|
private TimeSpan _position;
|
||||||
private T _value;
|
private T _value;
|
||||||
|
|
||||||
public LayerPropertyKeyframe(T value, TimeSpan position, Easings.Functions easingFunction, LayerProperty<T> layerProperty) : base(layerProperty)
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="LayerPropertyKeyframe{T}" /> class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value of the keyframe</param>
|
||||||
|
/// <param name="position">The position of this keyframe in the timeline</param>
|
||||||
|
/// <param name="easingFunction">The easing function applied on the value of the keyframe</param>
|
||||||
|
/// <param name="layerProperty">The layer property this keyframe is applied to</param>
|
||||||
|
public LayerPropertyKeyframe(T value, TimeSpan position, Easings.Functions easingFunction, LayerProperty<T> layerProperty)
|
||||||
{
|
{
|
||||||
_position = position;
|
_position = position;
|
||||||
|
|
||||||
Value = value;
|
Value = value;
|
||||||
LayerProperty = layerProperty;
|
LayerProperty = layerProperty;
|
||||||
EasingFunction = easingFunction;
|
EasingFunction = easingFunction;
|
||||||
@ -34,8 +46,11 @@ namespace Artemis.Core
|
|||||||
set => SetAndNotify(ref _value, value);
|
set => SetAndNotify(ref _value, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override TimeSpan Position
|
/// <summary>
|
||||||
|
/// The position of this keyframe in the timeline
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan Position
|
||||||
{
|
{
|
||||||
get => _position;
|
get => _position;
|
||||||
set
|
set
|
||||||
@ -45,8 +60,15 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <summary>
|
||||||
public override void Remove()
|
/// The easing function applied on the value of the keyframe
|
||||||
|
/// </summary>
|
||||||
|
public Easings.Functions EasingFunction { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the keyframe from the layer property
|
||||||
|
/// </summary>
|
||||||
|
public void Remove()
|
||||||
{
|
{
|
||||||
LayerProperty.RemoveKeyframe(this);
|
LayerProperty.RemoveKeyframe(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
using Artemis.Storage.Entities.Profile;
|
namespace Artemis.Core
|
||||||
|
|
||||||
namespace Artemis.Core
|
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public class ColorGradientLayerProperty : LayerProperty<ColorGradient>
|
public class ColorGradientLayerProperty : LayerProperty<ColorGradient>
|
||||||
@ -9,10 +7,12 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
KeyframesSupported = false;
|
KeyframesSupported = false;
|
||||||
DataBindingsSupported = false;
|
DataBindingsSupported = false;
|
||||||
|
|
||||||
|
BaseValueChanged += OnBaseValueChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implicitly converts an <see cref="ColorGradientLayerProperty" /> to a <see cref="ColorGradient"/>
|
/// Implicitly converts an <see cref="ColorGradientLayerProperty" /> to a <see cref="ColorGradient" />
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static implicit operator ColorGradient(ColorGradientLayerProperty p)
|
public static implicit operator ColorGradient(ColorGradientLayerProperty p)
|
||||||
{
|
{
|
||||||
@ -25,12 +25,11 @@ namespace Artemis.Core
|
|||||||
throw new ArtemisCoreException("Color Gradients do not support keyframes.");
|
throw new ArtemisCoreException("Color Gradients do not support keyframes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void ApplyToLayerProperty(PropertyEntity entity, LayerPropertyGroup layerPropertyGroup, bool fromStorage)
|
private void OnBaseValueChanged(object sender, LayerPropertyEventArgs<ColorGradient> e)
|
||||||
{
|
{
|
||||||
base.ApplyToLayerProperty(entity, layerPropertyGroup, fromStorage);
|
|
||||||
|
|
||||||
// Don't allow color gradients to be null
|
// Don't allow color gradients to be null
|
||||||
BaseValue ??= DefaultValue ?? new ColorGradient();
|
if (BaseValue == null)
|
||||||
|
BaseValue = DefaultValue ?? new ColorGradient();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -7,11 +7,10 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
internal SKColorLayerProperty()
|
internal SKColorLayerProperty()
|
||||||
{
|
{
|
||||||
RegisterDataBindingProperty(color => color.Alpha, new SKColorPartDataBindingConverter(SKColorPartDataBindingConverter.Channel.Alpha));
|
RegisterDataBindingProperty(color => color.Alpha, new SKColorArgbDataBindingConverter(SKColorArgbDataBindingConverter.Channel.Alpha));
|
||||||
RegisterDataBindingProperty(color => color.Red, new SKColorPartDataBindingConverter(SKColorPartDataBindingConverter.Channel.Red));
|
RegisterDataBindingProperty(color => color.Red, new SKColorArgbDataBindingConverter(SKColorArgbDataBindingConverter.Channel.Red));
|
||||||
RegisterDataBindingProperty(color => color.Green, new SKColorPartDataBindingConverter(SKColorPartDataBindingConverter.Channel.Green));
|
RegisterDataBindingProperty(color => color.Green, new SKColorArgbDataBindingConverter(SKColorArgbDataBindingConverter.Channel.Green));
|
||||||
RegisterDataBindingProperty(color => color.Blue, new SKColorPartDataBindingConverter(SKColorPartDataBindingConverter.Channel.Blue));
|
RegisterDataBindingProperty(color => color.Blue, new SKColorArgbDataBindingConverter(SKColorArgbDataBindingConverter.Channel.Blue));
|
||||||
RegisterDataBindingProperty(color => color.Hue, new SKColorPartDataBindingConverter(SKColorPartDataBindingConverter.Channel.Hue));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -7,8 +7,8 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
internal SKPointLayerProperty()
|
internal SKPointLayerProperty()
|
||||||
{
|
{
|
||||||
RegisterDataBindingProperty(point => point.X, new FloatDataBindingConverter());
|
RegisterDataBindingProperty(point => point.X, new FloatDataBindingConverter<SKPoint>());
|
||||||
RegisterDataBindingProperty(point => point.Y, new FloatDataBindingConverter());
|
RegisterDataBindingProperty(point => point.Y, new FloatDataBindingConverter<SKPoint>());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -7,8 +7,8 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
internal SKSizeLayerProperty()
|
internal SKSizeLayerProperty()
|
||||||
{
|
{
|
||||||
RegisterDataBindingProperty(size => size.Width, new FloatDataBindingConverter());
|
RegisterDataBindingProperty(size => size.Width, new FloatDataBindingConverter<SKSize>());
|
||||||
RegisterDataBindingProperty(size => size.Height, new FloatDataBindingConverter());
|
RegisterDataBindingProperty(size => size.Height, new FloatDataBindingConverter<SKSize>());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -2,54 +2,58 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using Artemis.Core.LayerBrushes;
|
using Artemis.Core.LayerBrushes;
|
||||||
using Artemis.Core.LayerEffects;
|
using Artemis.Core.LayerEffects;
|
||||||
using Artemis.Core.Properties;
|
using Artemis.Core.Properties;
|
||||||
using Artemis.Core.Services;
|
|
||||||
using Artemis.Storage.Entities.Profile;
|
using Artemis.Storage.Entities.Profile;
|
||||||
|
using Humanizer;
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
public abstract class LayerPropertyGroup : IDisposable
|
public abstract class LayerPropertyGroup : IDisposable
|
||||||
{
|
{
|
||||||
private readonly List<BaseLayerProperty> _layerProperties;
|
private readonly List<ILayerProperty> _layerProperties;
|
||||||
private readonly List<LayerPropertyGroup> _layerPropertyGroups;
|
private readonly List<LayerPropertyGroup> _layerPropertyGroups;
|
||||||
private ReadOnlyCollection<BaseLayerProperty> _allLayerProperties;
|
private bool _disposed;
|
||||||
private bool _isHidden;
|
private bool _isHidden;
|
||||||
|
|
||||||
protected LayerPropertyGroup()
|
protected LayerPropertyGroup()
|
||||||
{
|
{
|
||||||
_layerProperties = new List<BaseLayerProperty>();
|
_layerProperties = new List<ILayerProperty>();
|
||||||
_layerPropertyGroups = new List<LayerPropertyGroup>();
|
_layerPropertyGroups = new List<LayerPropertyGroup>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the profile element (such as layer or folder) this effect is applied to
|
/// Gets the description of this group
|
||||||
|
/// </summary>
|
||||||
|
public PropertyGroupDescriptionAttribute GroupDescription { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the info of the plugin this group is associated with
|
||||||
|
/// </summary>
|
||||||
|
public PluginInfo PluginInfo { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the profile element (such as layer or folder) this group is associated with
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public RenderProfileElement ProfileElement { get; internal set; }
|
public RenderProfileElement ProfileElement { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The parent group of this group
|
||||||
|
/// </summary>
|
||||||
|
public LayerPropertyGroup Parent { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The path of this property group
|
/// The path of this property group
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Path { get; internal set; }
|
public string Path { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The parent group of this layer property group, set after construction
|
|
||||||
/// </summary>
|
|
||||||
public LayerPropertyGroup Parent { get; internal set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether this property groups properties are all initialized
|
/// Gets whether this property groups properties are all initialized
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool PropertiesInitialized { get; private set; }
|
public bool PropertiesInitialized { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Used to declare that this property group doesn't belong to a plugin and should use the core plugin GUID
|
|
||||||
/// </summary>
|
|
||||||
public bool IsCorePropertyGroup { get; internal set; }
|
|
||||||
|
|
||||||
public PropertyGroupDescriptionAttribute GroupDescription { get; internal set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The layer brush this property group belongs to
|
/// The layer brush this property group belongs to
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -76,37 +80,45 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A list of all layer properties in this group
|
/// A list of all layer properties in this group
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ReadOnlyCollection<BaseLayerProperty> LayerProperties => _layerProperties.AsReadOnly();
|
public ReadOnlyCollection<ILayerProperty> LayerProperties => _layerProperties.AsReadOnly();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A list of al child groups in this group
|
/// A list of al child groups in this group
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ReadOnlyCollection<LayerPropertyGroup> LayerPropertyGroups => _layerPropertyGroups.AsReadOnly();
|
public ReadOnlyCollection<LayerPropertyGroup> LayerPropertyGroups => _layerPropertyGroups.AsReadOnly();
|
||||||
|
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
_disposed = true;
|
||||||
DisableProperties();
|
DisableProperties();
|
||||||
|
|
||||||
|
foreach (var layerProperty in _layerProperties)
|
||||||
|
layerProperty.Dispose();
|
||||||
foreach (var layerPropertyGroup in _layerPropertyGroups)
|
foreach (var layerPropertyGroup in _layerPropertyGroups)
|
||||||
layerPropertyGroup.Dispose();
|
layerPropertyGroup.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Recursively gets all layer properties on this group and any subgroups
|
/// Recursively gets all layer properties on this group and any subgroups
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
public IReadOnlyCollection<ILayerProperty> GetAllLayerProperties()
|
||||||
public IReadOnlyCollection<BaseLayerProperty> GetAllLayerProperties()
|
|
||||||
{
|
{
|
||||||
if (!PropertiesInitialized)
|
if (_disposed)
|
||||||
return new List<BaseLayerProperty>();
|
throw new ObjectDisposedException("LayerPropertyGroup");
|
||||||
if (_allLayerProperties != null)
|
|
||||||
return _allLayerProperties;
|
|
||||||
|
|
||||||
var result = new List<BaseLayerProperty>(LayerProperties);
|
if (!PropertiesInitialized)
|
||||||
|
return new List<ILayerProperty>();
|
||||||
|
|
||||||
|
var result = new List<ILayerProperty>(LayerProperties);
|
||||||
foreach (var layerPropertyGroup in LayerPropertyGroups)
|
foreach (var layerPropertyGroup in LayerPropertyGroups)
|
||||||
result.AddRange(layerPropertyGroup.GetAllLayerProperties());
|
result.AddRange(layerPropertyGroup.GetAllLayerProperties());
|
||||||
|
|
||||||
_allLayerProperties = result.AsReadOnly();
|
return result.AsReadOnly();
|
||||||
return _allLayerProperties;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -116,7 +128,7 @@ namespace Artemis.Core
|
|||||||
protected abstract void PopulateDefaults();
|
protected abstract void PopulateDefaults();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when the property group is deactivated
|
/// Called when the property group is aactivated
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected abstract void EnableProperties();
|
protected abstract void EnableProperties();
|
||||||
|
|
||||||
@ -125,19 +137,26 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected abstract void DisableProperties();
|
protected abstract void DisableProperties();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the property group and all its layer properties have been initialized
|
||||||
|
/// </summary>
|
||||||
protected virtual void OnPropertyGroupInitialized()
|
protected virtual void OnPropertyGroupInitialized()
|
||||||
{
|
{
|
||||||
PropertyGroupInitialized?.Invoke(this, EventArgs.Empty);
|
PropertyGroupInitialized?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void InitializeProperties(IRenderElementService renderElementService, RenderProfileElement profileElement, [NotNull] string path)
|
internal void Initialize(RenderProfileElement profileElement, [NotNull] string path, PluginInfo pluginInfo)
|
||||||
{
|
{
|
||||||
if (path == null)
|
if (path == null)
|
||||||
throw new ArgumentNullException(nameof(path));
|
throw new ArgumentNullException(nameof(path));
|
||||||
|
if (pluginInfo == null)
|
||||||
|
throw new ArgumentNullException(nameof(pluginInfo));
|
||||||
|
|
||||||
// Doubt this will happen but let's make sure
|
// Doubt this will happen but let's make sure
|
||||||
if (PropertiesInitialized)
|
if (PropertiesInitialized)
|
||||||
throw new ArtemisCoreException("Layer property group already initialized, wut");
|
throw new ArtemisCoreException("Layer property group already initialized, wut");
|
||||||
|
|
||||||
|
PluginInfo = pluginInfo;
|
||||||
ProfileElement = profileElement;
|
ProfileElement = profileElement;
|
||||||
Path = path.TrimEnd('.');
|
Path = path.TrimEnd('.');
|
||||||
|
|
||||||
@ -146,55 +165,21 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
var propertyDescription = Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyDescriptionAttribute));
|
var propertyDescription = Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyDescriptionAttribute));
|
||||||
if (propertyDescription != null)
|
if (propertyDescription != null)
|
||||||
{
|
InitializeProperty(propertyInfo, (PropertyDescriptionAttribute) propertyDescription);
|
||||||
if (!typeof(BaseLayerProperty).IsAssignableFrom(propertyInfo.PropertyType))
|
|
||||||
throw new ArtemisPluginException($"Layer property with PropertyDescription attribute must be of type LayerProperty at {path + propertyInfo.Name}");
|
|
||||||
|
|
||||||
var instance = (BaseLayerProperty) Activator.CreateInstance(propertyInfo.PropertyType, true);
|
|
||||||
if (instance == null)
|
|
||||||
throw new ArtemisPluginException($"Failed to create instance of layer property at {path + propertyInfo.Name}");
|
|
||||||
|
|
||||||
instance.ProfileElement = profileElement;
|
|
||||||
instance.Parent = this;
|
|
||||||
instance.PropertyDescription = (PropertyDescriptionAttribute) propertyDescription;
|
|
||||||
if (instance.PropertyDescription.DisableKeyframes)
|
|
||||||
instance.KeyframesSupported = false;
|
|
||||||
|
|
||||||
InitializeProperty(profileElement, path + propertyInfo.Name, instance);
|
|
||||||
|
|
||||||
propertyInfo.SetValue(this, instance);
|
|
||||||
_layerProperties.Add(instance);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var propertyGroupDescription = Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyGroupDescriptionAttribute));
|
var propertyGroupDescription = Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyGroupDescriptionAttribute));
|
||||||
if (propertyGroupDescription != null)
|
if (propertyGroupDescription != null)
|
||||||
{
|
InitializeChildGroup(propertyInfo, (PropertyGroupDescriptionAttribute) propertyGroupDescription);
|
||||||
if (!typeof(LayerPropertyGroup).IsAssignableFrom(propertyInfo.PropertyType))
|
|
||||||
throw new ArtemisPluginException("Layer property with PropertyGroupDescription attribute must be of type LayerPropertyGroup");
|
|
||||||
|
|
||||||
var instance = (LayerPropertyGroup) Activator.CreateInstance(propertyInfo.PropertyType);
|
|
||||||
if (instance == null)
|
|
||||||
throw new ArtemisPluginException($"Failed to create instance of layer property group at {path + propertyInfo.Name}");
|
|
||||||
|
|
||||||
instance.Parent = this;
|
|
||||||
instance.GroupDescription = (PropertyGroupDescriptionAttribute) propertyGroupDescription;
|
|
||||||
instance.LayerBrush = LayerBrush;
|
|
||||||
instance.LayerEffect = LayerEffect;
|
|
||||||
instance.InitializeProperties(renderElementService, profileElement, $"{path}{propertyInfo.Name}.");
|
|
||||||
|
|
||||||
propertyInfo.SetValue(this, instance);
|
|
||||||
_layerPropertyGroups.Add(instance);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request the property group to populate defaults
|
// Request the property group to populate defaults
|
||||||
PopulateDefaults();
|
PopulateDefaults();
|
||||||
|
|
||||||
// Apply the newly populated defaults
|
// Load the layer properties after defaults have been applied
|
||||||
foreach (var layerProperty in _layerProperties.Where(p => !p.IsLoadedFromStorage))
|
foreach (var layerProperty in _layerProperties)
|
||||||
layerProperty.ApplyDefaultValue();
|
layerProperty.Load();
|
||||||
|
|
||||||
EnableProperties();
|
EnableProperties();
|
||||||
PropertiesInitialized = true;
|
PropertiesInitialized = true;
|
||||||
@ -206,25 +191,11 @@ namespace Artemis.Core
|
|||||||
if (!PropertiesInitialized)
|
if (!PropertiesInitialized)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Get all properties with a PropertyDescriptionAttribute
|
foreach (var layerProperty in LayerProperties)
|
||||||
foreach (var propertyInfo in GetType().GetProperties())
|
layerProperty.Save();
|
||||||
{
|
|
||||||
var propertyDescription = Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyDescriptionAttribute));
|
foreach (var layerPropertyGroup in LayerPropertyGroups)
|
||||||
if (propertyDescription != null)
|
layerPropertyGroup.ApplyToEntity();
|
||||||
{
|
|
||||||
var layerProperty = (BaseLayerProperty) propertyInfo.GetValue(this);
|
|
||||||
layerProperty.ApplyToEntity();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var propertyGroupDescription = Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyGroupDescriptionAttribute));
|
|
||||||
if (propertyGroupDescription != null)
|
|
||||||
{
|
|
||||||
var layerPropertyGroup = (LayerPropertyGroup) propertyInfo.GetValue(this);
|
|
||||||
layerPropertyGroup.ApplyToEntity();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Update(double deltaTime)
|
internal void Update(double deltaTime)
|
||||||
@ -234,32 +205,63 @@ namespace Artemis.Core
|
|||||||
OnPropertyGroupUpdating(new LayerPropertyGroupUpdatingEventArgs(deltaTime));
|
OnPropertyGroupUpdating(new LayerPropertyGroupUpdatingEventArgs(deltaTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeProperty(RenderProfileElement profileElement, string path, BaseLayerProperty instance)
|
private void InitializeProperty(PropertyInfo propertyInfo, PropertyDescriptionAttribute propertyDescription)
|
||||||
{
|
{
|
||||||
Guid pluginGuid;
|
var path = Path + ".";
|
||||||
if (IsCorePropertyGroup || instance.IsCoreProperty)
|
|
||||||
pluginGuid = Constants.CorePluginInfo.Guid;
|
|
||||||
else if (instance.Parent.LayerBrush != null)
|
|
||||||
pluginGuid = instance.Parent.LayerBrush.PluginInfo.Guid;
|
|
||||||
else
|
|
||||||
pluginGuid = instance.Parent.LayerEffect.PluginInfo.Guid;
|
|
||||||
|
|
||||||
var entity = profileElement.RenderElementEntity.PropertyEntities.FirstOrDefault(p => p.PluginGuid == pluginGuid && p.Path == path);
|
if (!typeof(ILayerProperty).IsAssignableFrom(propertyInfo.PropertyType))
|
||||||
var fromStorage = true;
|
throw new ArtemisPluginException($"Layer property with PropertyDescription attribute must be of type LayerProperty at {path + propertyInfo.Name}");
|
||||||
|
|
||||||
|
var instance = (ILayerProperty) Activator.CreateInstance(propertyInfo.PropertyType, true);
|
||||||
|
if (instance == null)
|
||||||
|
throw new ArtemisPluginException($"Failed to create instance of layer property at {path + propertyInfo.Name}");
|
||||||
|
|
||||||
|
// Ensure the description has a name, if not this is a good point to set it based on the property info
|
||||||
|
if (string.IsNullOrWhiteSpace(propertyDescription.Name))
|
||||||
|
propertyDescription.Name = propertyInfo.Name.Humanize();
|
||||||
|
|
||||||
|
var entity = GetPropertyEntity(ProfileElement, path + propertyInfo.Name, out var fromStorage);
|
||||||
|
instance.Initialize(ProfileElement, this, entity, fromStorage, propertyDescription);
|
||||||
|
propertyInfo.SetValue(this, instance);
|
||||||
|
_layerProperties.Add(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeChildGroup(PropertyInfo propertyInfo, PropertyGroupDescriptionAttribute propertyGroupDescription)
|
||||||
|
{
|
||||||
|
var path = Path + ".";
|
||||||
|
|
||||||
|
if (!typeof(LayerPropertyGroup).IsAssignableFrom(propertyInfo.PropertyType))
|
||||||
|
throw new ArtemisPluginException("Layer property with PropertyGroupDescription attribute must be of type LayerPropertyGroup");
|
||||||
|
|
||||||
|
var instance = (LayerPropertyGroup) Activator.CreateInstance(propertyInfo.PropertyType);
|
||||||
|
if (instance == null)
|
||||||
|
throw new ArtemisPluginException($"Failed to create instance of layer property group at {path + propertyInfo.Name}");
|
||||||
|
|
||||||
|
// Ensure the description has a name, if not this is a good point to set it based on the property info
|
||||||
|
if (string.IsNullOrWhiteSpace(propertyGroupDescription.Name))
|
||||||
|
propertyGroupDescription.Name = propertyInfo.Name.Humanize();
|
||||||
|
|
||||||
|
instance.Parent = this;
|
||||||
|
instance.GroupDescription = propertyGroupDescription;
|
||||||
|
instance.LayerBrush = LayerBrush;
|
||||||
|
instance.LayerEffect = LayerEffect;
|
||||||
|
instance.Initialize(ProfileElement, $"{path}{propertyInfo.Name}.", PluginInfo);
|
||||||
|
|
||||||
|
propertyInfo.SetValue(this, instance);
|
||||||
|
_layerPropertyGroups.Add(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PropertyEntity GetPropertyEntity(RenderProfileElement profileElement, string path, out bool fromStorage)
|
||||||
|
{
|
||||||
|
var entity = profileElement.RenderElementEntity.PropertyEntities.FirstOrDefault(p => p.PluginGuid == PluginInfo.Guid && p.Path == path);
|
||||||
|
fromStorage = entity != null;
|
||||||
if (entity == null)
|
if (entity == null)
|
||||||
{
|
{
|
||||||
fromStorage = false;
|
entity = new PropertyEntity {PluginGuid = PluginInfo.Guid, Path = path};
|
||||||
entity = new PropertyEntity {PluginGuid = pluginGuid, Path = path};
|
|
||||||
profileElement.RenderElementEntity.PropertyEntities.Add(entity);
|
profileElement.RenderElementEntity.PropertyEntities.Add(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
instance.ApplyToLayerProperty(entity, this, fromStorage);
|
return entity;
|
||||||
instance.BaseValueChanged += InstanceOnBaseValueChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InstanceOnBaseValueChanged(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
OnLayerPropertyBaseValueChanged(new LayerPropertyEventArgs((BaseLayerProperty) sender));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Events
|
#region Events
|
||||||
@ -282,17 +284,17 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler VisibilityChanged;
|
public event EventHandler VisibilityChanged;
|
||||||
|
|
||||||
protected virtual void OnPropertyGroupUpdating(LayerPropertyGroupUpdatingEventArgs e)
|
internal virtual void OnPropertyGroupUpdating(LayerPropertyGroupUpdatingEventArgs e)
|
||||||
{
|
{
|
||||||
PropertyGroupUpdating?.Invoke(this, e);
|
PropertyGroupUpdating?.Invoke(this, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnVisibilityChanged()
|
internal virtual void OnVisibilityChanged()
|
||||||
{
|
{
|
||||||
VisibilityChanged?.Invoke(this, EventArgs.Empty);
|
VisibilityChanged?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnLayerPropertyBaseValueChanged(LayerPropertyEventArgs e)
|
internal virtual void OnLayerPropertyBaseValueChanged(LayerPropertyEventArgs e)
|
||||||
{
|
{
|
||||||
LayerPropertyBaseValueChanged?.Invoke(this, e);
|
LayerPropertyBaseValueChanged?.Invoke(this, e);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,17 +16,19 @@ namespace Artemis.Core
|
|||||||
ProfileEntity = new ProfileEntity();
|
ProfileEntity = new ProfileEntity();
|
||||||
EntityId = Guid.NewGuid();
|
EntityId = Guid.NewGuid();
|
||||||
|
|
||||||
|
Profile = this;
|
||||||
Module = module;
|
Module = module;
|
||||||
Name = name;
|
Name = name;
|
||||||
UndoStack = new Stack<string>();
|
UndoStack = new Stack<string>();
|
||||||
RedoStack = new Stack<string>();
|
RedoStack = new Stack<string>();
|
||||||
|
|
||||||
AddChild(new Folder(this, this, "Root folder"));
|
var _ = new Folder(this, "Root folder");
|
||||||
ApplyToEntity();
|
Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Profile(ProfileModule module, ProfileEntity profileEntity)
|
internal Profile(ProfileModule module, ProfileEntity profileEntity)
|
||||||
{
|
{
|
||||||
|
Profile = this;
|
||||||
ProfileEntity = profileEntity;
|
ProfileEntity = profileEntity;
|
||||||
EntityId = profileEntity.Id;
|
EntityId = profileEntity.Id;
|
||||||
|
|
||||||
@ -34,7 +36,7 @@ namespace Artemis.Core
|
|||||||
UndoStack = new Stack<string>();
|
UndoStack = new Stack<string>();
|
||||||
RedoStack = new Stack<string>();
|
RedoStack = new Stack<string>();
|
||||||
|
|
||||||
ApplyToProfile();
|
Load();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProfileModule Module { get; }
|
public ProfileModule Module { get; }
|
||||||
@ -86,7 +88,7 @@ namespace Artemis.Core
|
|||||||
return (Folder) Children.Single();
|
return (Folder) Children.Single();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ApplyToProfile()
|
internal override void Load()
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
throw new ObjectDisposedException("Profile");
|
throw new ObjectDisposedException("Profile");
|
||||||
@ -103,7 +105,9 @@ namespace Artemis.Core
|
|||||||
// Populate the profile starting at the root, the rest is populated recursively
|
// Populate the profile starting at the root, the rest is populated recursively
|
||||||
var rootFolder = ProfileEntity.Folders.FirstOrDefault(f => f.ParentId == EntityId);
|
var rootFolder = ProfileEntity.Folders.FirstOrDefault(f => f.ParentId == EntityId);
|
||||||
if (rootFolder == null)
|
if (rootFolder == null)
|
||||||
AddChild(new Folder(this, this, "Root folder"));
|
{
|
||||||
|
var _ = new Folder(this, "Root folder");
|
||||||
|
}
|
||||||
else
|
else
|
||||||
AddChild(new Folder(this, this, rootFolder));
|
AddChild(new Folder(this, this, rootFolder));
|
||||||
}
|
}
|
||||||
@ -129,7 +133,7 @@ namespace Artemis.Core
|
|||||||
_disposed = true;
|
_disposed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void ApplyToEntity()
|
internal override void Save()
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
throw new ObjectDisposedException("Profile");
|
throw new ObjectDisposedException("Profile");
|
||||||
@ -140,7 +144,7 @@ namespace Artemis.Core
|
|||||||
ProfileEntity.IsActive = IsActivated;
|
ProfileEntity.IsActive = IsActivated;
|
||||||
|
|
||||||
foreach (var profileElement in Children)
|
foreach (var profileElement in Children)
|
||||||
profileElement.ApplyToEntity();
|
profileElement.Save();
|
||||||
|
|
||||||
ProfileEntity.Folders.Clear();
|
ProfileEntity.Folders.Clear();
|
||||||
ProfileEntity.Folders.AddRange(GetAllFolders().Select(f => f.FolderEntity));
|
ProfileEntity.Folders.AddRange(GetAllFolders().Select(f => f.FolderEntity));
|
||||||
|
|||||||
@ -23,18 +23,27 @@ namespace Artemis.Core
|
|||||||
ChildrenList = new List<ProfileElement>();
|
ChildrenList = new List<ProfileElement>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the unique ID of this profile element
|
||||||
|
/// </summary>
|
||||||
public Guid EntityId
|
public Guid EntityId
|
||||||
{
|
{
|
||||||
get => _entityId;
|
get => _entityId;
|
||||||
internal set => SetAndNotify(ref _entityId, value);
|
internal set => SetAndNotify(ref _entityId, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the profile this element belongs to
|
||||||
|
/// </summary>
|
||||||
public Profile Profile
|
public Profile Profile
|
||||||
{
|
{
|
||||||
get => _profile;
|
get => _profile;
|
||||||
internal set => SetAndNotify(ref _profile, value);
|
internal set => SetAndNotify(ref _profile, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the parent of this element
|
||||||
|
/// </summary>
|
||||||
public ProfileElement Parent
|
public ProfileElement Parent
|
||||||
{
|
{
|
||||||
get => _parent;
|
get => _parent;
|
||||||
@ -73,12 +82,6 @@ namespace Artemis.Core
|
|||||||
set => SetAndNotify(ref _enabled, value);
|
set => SetAndNotify(ref _enabled, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the element
|
/// Updates the element
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -90,39 +93,13 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo);
|
public abstract void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo);
|
||||||
|
|
||||||
public List<Folder> GetAllFolders()
|
/// <inheritdoc />
|
||||||
|
public override string ToString()
|
||||||
{
|
{
|
||||||
if (_disposed)
|
return $"{nameof(EntityId)}: {EntityId}, {nameof(Order)}: {Order}, {nameof(Name)}: {Name}";
|
||||||
throw new ObjectDisposedException(GetType().Name);
|
|
||||||
|
|
||||||
var folders = new List<Folder>();
|
|
||||||
foreach (var childFolder in Children.Where(c => c is Folder).Cast<Folder>())
|
|
||||||
{
|
|
||||||
// Add all folders in this element
|
|
||||||
folders.Add(childFolder);
|
|
||||||
// Add all folders in folders inside this element
|
|
||||||
folders.AddRange(childFolder.GetAllFolders());
|
|
||||||
}
|
|
||||||
|
|
||||||
return folders;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Layer> GetAllLayers()
|
#region Hierarchy
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException(GetType().Name);
|
|
||||||
|
|
||||||
var layers = new List<Layer>();
|
|
||||||
|
|
||||||
// Add all layers in this element
|
|
||||||
layers.AddRange(Children.Where(c => c is Layer).Cast<Layer>());
|
|
||||||
|
|
||||||
// Add all layers in folders inside this element
|
|
||||||
foreach (var childFolder in Children.Where(c => c is Folder).Cast<Folder>())
|
|
||||||
layers.AddRange(childFolder.GetAllLayers());
|
|
||||||
|
|
||||||
return layers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a profile element to the <see cref="Children" /> collection, optionally at the given position (1-based)
|
/// Adds a profile element to the <see cref="Children" /> collection, optionally at the given position (1-based)
|
||||||
@ -133,9 +110,12 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
throw new ObjectDisposedException(GetType().Name);
|
throw new ObjectDisposedException(GetType().Name);
|
||||||
|
|
||||||
lock (ChildrenList)
|
lock (ChildrenList)
|
||||||
{
|
{
|
||||||
|
if (ChildrenList.Contains(child))
|
||||||
|
return;
|
||||||
|
|
||||||
// Add to the end of the list
|
// Add to the end of the list
|
||||||
if (order == null)
|
if (order == null)
|
||||||
{
|
{
|
||||||
@ -189,9 +169,64 @@ namespace Artemis.Core
|
|||||||
OnChildRemoved();
|
OnChildRemoved();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
/// <summary>
|
||||||
|
/// Returns a flattened list of all child folders
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public List<Folder> GetAllFolders()
|
||||||
{
|
{
|
||||||
return $"{nameof(EntityId)}: {EntityId}, {nameof(Order)}: {Order}, {nameof(Name)}: {Name}";
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException(GetType().Name);
|
||||||
|
|
||||||
|
var folders = new List<Folder>();
|
||||||
|
foreach (var childFolder in Children.Where(c => c is Folder).Cast<Folder>())
|
||||||
|
{
|
||||||
|
// Add all folders in this element
|
||||||
|
folders.Add(childFolder);
|
||||||
|
// Add all folders in folders inside this element
|
||||||
|
folders.AddRange(childFolder.GetAllFolders());
|
||||||
|
}
|
||||||
|
|
||||||
|
return folders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a flattened list of all child layers
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public List<Layer> GetAllLayers()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException(GetType().Name);
|
||||||
|
|
||||||
|
var layers = new List<Layer>();
|
||||||
|
|
||||||
|
// Add all layers in this element
|
||||||
|
layers.AddRange(Children.Where(c => c is Layer).Cast<Layer>());
|
||||||
|
|
||||||
|
// Add all layers in folders inside this element
|
||||||
|
foreach (var childFolder in Children.Where(c => c is Folder).Cast<Folder>())
|
||||||
|
layers.AddRange(childFolder.GetAllLayers());
|
||||||
|
|
||||||
|
return layers;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Storage
|
||||||
|
|
||||||
|
internal abstract void Load();
|
||||||
|
internal abstract void Save();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
@ -201,10 +236,7 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
#endregion
|
||||||
/// Applies the profile element's properties to the underlying storage entity
|
|
||||||
/// </summary>
|
|
||||||
internal abstract void ApplyToEntity();
|
|
||||||
|
|
||||||
#region Events
|
#region Events
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Artemis.Core.LayerEffects;
|
using Artemis.Core.LayerEffects;
|
||||||
|
using Artemis.Core.LayerEffects.Placeholder;
|
||||||
using Artemis.Core.Properties;
|
using Artemis.Core.Properties;
|
||||||
using Artemis.Storage.Entities.Profile;
|
using Artemis.Storage.Entities.Profile;
|
||||||
using Artemis.Storage.Entities.Profile.Abstract;
|
using Artemis.Storage.Entities.Profile.Abstract;
|
||||||
@ -12,36 +13,33 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
public abstract class RenderProfileElement : ProfileElement
|
public abstract class RenderProfileElement : ProfileElement
|
||||||
{
|
{
|
||||||
/// <summary>
|
protected RenderProfileElement()
|
||||||
/// Returns a list of all keyframes on all properties and effects of this layer
|
|
||||||
/// </summary>
|
|
||||||
public virtual List<BaseLayerPropertyKeyframe> GetAllKeyframes()
|
|
||||||
{
|
{
|
||||||
var keyframes = new List<BaseLayerPropertyKeyframe>();
|
LayerEffectStore.LayerEffectAdded += LayerEffectStoreOnLayerEffectAdded;
|
||||||
foreach (var layerEffect in LayerEffects)
|
LayerEffectStore.LayerEffectRemoved += LayerEffectStoreOnLayerEffectRemoved;
|
||||||
{
|
|
||||||
foreach (var baseLayerProperty in layerEffect.BaseProperties.GetAllLayerProperties())
|
|
||||||
keyframes.AddRange(baseLayerProperty.BaseKeyframes);
|
|
||||||
}
|
|
||||||
|
|
||||||
return keyframes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void ApplyRenderElementDefaults()
|
internal void ApplyRenderElementDefaults()
|
||||||
{
|
{
|
||||||
MainSegmentLength = TimeSpan.FromSeconds(5);
|
MainSegmentLength = TimeSpan.FromSeconds(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void ApplyRenderElementEntity()
|
internal void LoadRenderElement()
|
||||||
{
|
{
|
||||||
StartSegmentLength = RenderElementEntity.StartSegmentLength;
|
StartSegmentLength = RenderElementEntity.StartSegmentLength;
|
||||||
MainSegmentLength = RenderElementEntity.MainSegmentLength;
|
MainSegmentLength = RenderElementEntity.MainSegmentLength;
|
||||||
EndSegmentLength = RenderElementEntity.EndSegmentLength;
|
EndSegmentLength = RenderElementEntity.EndSegmentLength;
|
||||||
DisplayContinuously = RenderElementEntity.DisplayContinuously;
|
DisplayContinuously = RenderElementEntity.DisplayContinuously;
|
||||||
AlwaysFinishTimeline = RenderElementEntity.AlwaysFinishTimeline;
|
AlwaysFinishTimeline = RenderElementEntity.AlwaysFinishTimeline;
|
||||||
|
|
||||||
|
DisplayConditionGroup = RenderElementEntity.RootDisplayCondition != null
|
||||||
|
? new DisplayConditionGroup(null, RenderElementEntity.RootDisplayCondition)
|
||||||
|
: new DisplayConditionGroup(null);
|
||||||
|
|
||||||
|
ActivateEffects();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void ApplyRenderElementToEntity()
|
internal void SaveRenderElement()
|
||||||
{
|
{
|
||||||
RenderElementEntity.StartSegmentLength = StartSegmentLength;
|
RenderElementEntity.StartSegmentLength = StartSegmentLength;
|
||||||
RenderElementEntity.MainSegmentLength = MainSegmentLength;
|
RenderElementEntity.MainSegmentLength = MainSegmentLength;
|
||||||
@ -55,8 +53,8 @@ namespace Artemis.Core
|
|||||||
var layerEffectEntity = new LayerEffectEntity
|
var layerEffectEntity = new LayerEffectEntity
|
||||||
{
|
{
|
||||||
Id = layerEffect.EntityId,
|
Id = layerEffect.EntityId,
|
||||||
PluginGuid = layerEffect.PluginInfo.Guid,
|
PluginGuid = layerEffect.Descriptor.PlaceholderFor ?? layerEffect.PluginInfo.Guid,
|
||||||
EffectType = layerEffect.GetType().Name,
|
EffectType = layerEffect.GetEffectTypeName(),
|
||||||
Name = layerEffect.Name,
|
Name = layerEffect.Name,
|
||||||
Enabled = layerEffect.Enabled,
|
Enabled = layerEffect.Enabled,
|
||||||
HasBeenRenamed = layerEffect.HasBeenRenamed,
|
HasBeenRenamed = layerEffect.HasBeenRenamed,
|
||||||
@ -65,6 +63,10 @@ namespace Artemis.Core
|
|||||||
RenderElementEntity.LayerEffects.Add(layerEffectEntity);
|
RenderElementEntity.LayerEffects.Add(layerEffectEntity);
|
||||||
layerEffect.BaseProperties.ApplyToEntity();
|
layerEffect.BaseProperties.ApplyToEntity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Conditions
|
||||||
|
RenderElementEntity.RootDisplayCondition = DisplayConditionGroup?.Entity;
|
||||||
|
DisplayConditionGroup?.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Properties
|
#region Properties
|
||||||
@ -211,7 +213,6 @@ namespace Artemis.Core
|
|||||||
return (TimelinePosition - oldPosition).TotalSeconds;
|
return (TimelinePosition - oldPosition).TotalSeconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Overrides the progress of the element
|
/// Overrides the progress of the element
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -221,7 +222,7 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Effects
|
#region Effect management
|
||||||
|
|
||||||
protected List<BaseLayerEffect> _layerEffects;
|
protected List<BaseLayerEffect> _layerEffects;
|
||||||
|
|
||||||
@ -230,13 +231,45 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ReadOnlyCollection<BaseLayerEffect> LayerEffects => _layerEffects.AsReadOnly();
|
public ReadOnlyCollection<BaseLayerEffect> LayerEffects => _layerEffects.AsReadOnly();
|
||||||
|
|
||||||
internal void RemoveLayerEffect([NotNull] BaseLayerEffect effect)
|
/// <summary>
|
||||||
|
/// Adds a the layer effect described inthe provided <paramref name="descriptor" />
|
||||||
|
/// </summary>
|
||||||
|
public void AddLayerEffect(LayerEffectDescriptor descriptor)
|
||||||
|
{
|
||||||
|
if (descriptor == null)
|
||||||
|
throw new ArgumentNullException(nameof(descriptor));
|
||||||
|
|
||||||
|
var entity = new LayerEffectEntity
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
Enabled = true,
|
||||||
|
Order = LayerEffects.Count + 1
|
||||||
|
};
|
||||||
|
descriptor.CreateInstance(this, entity);
|
||||||
|
|
||||||
|
OrderEffects();
|
||||||
|
OnLayerEffectsUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the provided layer
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="effect"></param>
|
||||||
|
public void RemoveLayerEffect([NotNull] BaseLayerEffect effect)
|
||||||
{
|
{
|
||||||
if (effect == null) throw new ArgumentNullException(nameof(effect));
|
if (effect == null) throw new ArgumentNullException(nameof(effect));
|
||||||
|
|
||||||
DeactivateLayerEffect(effect);
|
// Remove the effect from the layer and dispose it
|
||||||
|
_layerEffects.Remove(effect);
|
||||||
|
effect.Dispose();
|
||||||
|
|
||||||
// Update the order on the remaining effects
|
// Update the order on the remaining effects
|
||||||
|
OrderEffects();
|
||||||
|
OnLayerEffectsUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OrderEffects()
|
||||||
|
{
|
||||||
var index = 0;
|
var index = 0;
|
||||||
foreach (var baseLayerEffect in LayerEffects.OrderBy(e => e.Order))
|
foreach (var baseLayerEffect in LayerEffects.OrderBy(e => e.Order))
|
||||||
{
|
{
|
||||||
@ -244,23 +277,69 @@ namespace Artemis.Core
|
|||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_layerEffects.Sort((a, b) => a.Order.CompareTo(b.Order));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ActivateEffects()
|
||||||
|
{
|
||||||
|
foreach (var layerEffectEntity in RenderElementEntity.LayerEffects)
|
||||||
|
{
|
||||||
|
// 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.PlaceholderFor == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var descriptor = LayerEffectStore.Get(layerEffectEntity.PluginGuid, layerEffectEntity.EffectType)?.LayerEffectDescriptor;
|
||||||
|
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(layerEffectEntity.PluginGuid);
|
||||||
|
descriptor.CreateInstance(this, layerEffectEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OrderEffects();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal void ActivateLayerEffect(BaseLayerEffect layerEffect)
|
||||||
|
{
|
||||||
|
_layerEffects.Add(layerEffect);
|
||||||
OnLayerEffectsUpdated();
|
OnLayerEffectsUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AddLayerEffect([NotNull] BaseLayerEffect effect)
|
private void LayerEffectStoreOnLayerEffectRemoved(object sender, LayerEffectStoreEvent e)
|
||||||
{
|
{
|
||||||
if (effect == null) throw new ArgumentNullException(nameof(effect));
|
// If effects provided by the plugin are on the element, replace them with placeholders
|
||||||
_layerEffects.Add(effect);
|
var pluginEffects = _layerEffects.Where(ef => ef.Descriptor.LayerEffectProvider != null &&
|
||||||
OnLayerEffectsUpdated();
|
ef.PluginInfo.Guid == e.Registration.Plugin.PluginInfo.Guid).ToList();
|
||||||
|
foreach (var pluginEffect in pluginEffects)
|
||||||
|
{
|
||||||
|
var entity = RenderElementEntity.LayerEffects.First(en => en.Id == pluginEffect.EntityId);
|
||||||
|
_layerEffects.Remove(pluginEffect);
|
||||||
|
pluginEffect.Dispose();
|
||||||
|
|
||||||
|
var descriptor = PlaceholderLayerEffectDescriptor.Create(pluginEffect.PluginInfo.Guid);
|
||||||
|
descriptor.CreateInstance(this, entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void DeactivateLayerEffect([NotNull] BaseLayerEffect effect)
|
private void LayerEffectStoreOnLayerEffectAdded(object sender, LayerEffectStoreEvent e)
|
||||||
{
|
{
|
||||||
if (effect == null) throw new ArgumentNullException(nameof(effect));
|
if (RenderElementEntity.LayerEffects.Any(ef => ef.PluginGuid == e.Registration.Plugin.PluginInfo.Guid))
|
||||||
|
ActivateEffects();
|
||||||
// Remove the effect from the layer and dispose it
|
|
||||||
_layerEffects.Remove(effect);
|
|
||||||
effect.Dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -301,6 +380,21 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
LayerEffectStore.LayerEffectAdded -= LayerEffectStoreOnLayerEffectAdded;
|
||||||
|
LayerEffectStore.LayerEffectRemoved -= LayerEffectStoreOnLayerEffectRemoved;
|
||||||
|
|
||||||
|
foreach (var baseLayerEffect in LayerEffects)
|
||||||
|
baseLayerEffect.Dispose();
|
||||||
|
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Events
|
#region Events
|
||||||
|
|
||||||
public event EventHandler LayerEffectsUpdated;
|
public event EventHandler LayerEffectsUpdated;
|
||||||
@ -311,17 +405,5 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns all the layer properties of this profile element
|
|
||||||
/// </summary>
|
|
||||||
public virtual List<BaseLayerProperty> GetAllLayerProperties()
|
|
||||||
{
|
|
||||||
var result = new List<BaseLayerProperty>();
|
|
||||||
foreach (var baseLayerEffect in LayerEffects)
|
|
||||||
result.AddRange(baseLayerEffect.BaseProperties.GetAllLayerProperties());
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -22,6 +22,12 @@ namespace Artemis.Core.DataModelExpansions
|
|||||||
[DataModelIgnore]
|
[DataModelIgnore]
|
||||||
public DataModelPropertyAttribute DataModelDescription { get; internal set; }
|
public DataModelPropertyAttribute DataModelDescription { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the is expansion status indicating whether this data model expands the main data model
|
||||||
|
/// </summary>
|
||||||
|
[DataModelIgnore]
|
||||||
|
public bool IsExpansion { get; internal set; }
|
||||||
|
|
||||||
public bool ContainsPath(string path)
|
public bool ContainsPath(string path)
|
||||||
{
|
{
|
||||||
var parts = path.Split('.');
|
var parts = path.Split('.');
|
||||||
|
|||||||
@ -10,7 +10,8 @@ namespace Artemis.Core.DataModelExpansions
|
|||||||
public abstract class DataModelExpansion<T> : BaseDataModelExpansion where T : DataModel
|
public abstract class DataModelExpansion<T> : BaseDataModelExpansion where T : DataModel
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The data model driving this module
|
/// The main data model of this data model expansion
|
||||||
|
/// <para>Note: This default data model is automatically registered upon plugin enable</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public T DataModel
|
public T DataModel
|
||||||
{
|
{
|
||||||
|
|||||||
@ -105,7 +105,7 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
|
|
||||||
// Not only is this needed to initialize properties on the layer brushes, it also prevents implementing anything
|
// Not only is this needed to initialize properties on the layer brushes, it also prevents implementing anything
|
||||||
// but LayerBrush<T> and RgbNetLayerBrush<T> outside the core
|
// but LayerBrush<T> and RgbNetLayerBrush<T> outside the core
|
||||||
internal abstract void Initialize(IRenderElementService renderElementService);
|
internal abstract void Initialize();
|
||||||
|
|
||||||
internal abstract void InternalRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint);
|
internal abstract void InternalRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint);
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using Artemis.Core.Services;
|
|
||||||
|
|
||||||
namespace Artemis.Core.LayerBrushes
|
namespace Artemis.Core.LayerBrushes
|
||||||
{
|
{
|
||||||
@ -33,11 +32,12 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
internal set => _properties = value;
|
internal set => _properties = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void InitializeProperties(IRenderElementService renderElementService)
|
internal void InitializeProperties()
|
||||||
{
|
{
|
||||||
Properties = Activator.CreateInstance<T>();
|
Properties = Activator.CreateInstance<T>();
|
||||||
|
Properties.GroupDescription ??= new PropertyGroupDescriptionAttribute {Name = Descriptor.DisplayName, Description = Descriptor.Description};
|
||||||
Properties.LayerBrush = this;
|
Properties.LayerBrush = this;
|
||||||
Properties.InitializeProperties(renderElementService, Layer, "LayerBrush.");
|
Properties.Initialize(Layer, "LayerBrush.", PluginInfo);
|
||||||
PropertiesInitialized = true;
|
PropertiesInitialized = true;
|
||||||
|
|
||||||
EnableLayerBrush();
|
EnableLayerBrush();
|
||||||
|
|||||||
@ -26,9 +26,9 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
Render(canvas, canvasInfo, path, paint);
|
Render(canvas, canvasInfo, path, paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Initialize(IRenderElementService renderElementService)
|
internal override void Initialize()
|
||||||
{
|
{
|
||||||
InitializeProperties(renderElementService);
|
InitializeProperties();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,4 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Artemis.Core.Services;
|
||||||
|
using Ninject;
|
||||||
|
|
||||||
namespace Artemis.Core.LayerBrushes
|
namespace Artemis.Core.LayerBrushes
|
||||||
{
|
{
|
||||||
@ -41,5 +43,30 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
/// The plugin that provided this <see cref="LayerBrushDescriptor" />
|
/// The plugin that provided this <see cref="LayerBrushDescriptor" />
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public LayerBrushProvider LayerBrushProvider { get; }
|
public LayerBrushProvider LayerBrushProvider { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an instance of the described brush and applies it to the layer
|
||||||
|
/// </summary>
|
||||||
|
internal void CreateInstance(Layer layer)
|
||||||
|
{
|
||||||
|
if (layer.LayerBrush != null)
|
||||||
|
throw new ArtemisCoreException("Layer already has an instantiated layer brush");
|
||||||
|
|
||||||
|
var brush = (BaseLayerBrush) CoreService.Kernel.Get(LayerBrushType);
|
||||||
|
brush.Layer = layer;
|
||||||
|
brush.Descriptor = this;
|
||||||
|
brush.Initialize();
|
||||||
|
brush.Update(0);
|
||||||
|
|
||||||
|
layer.LayerBrush = brush;
|
||||||
|
layer.OnLayerBrushUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MatchesLayerBrushReference(LayerBrushReference reference)
|
||||||
|
{
|
||||||
|
if (reference == null)
|
||||||
|
return false;
|
||||||
|
return LayerBrushProvider.PluginInfo.Guid == reference.BrushPluginGuid && LayerBrushType.Name == reference.BrushType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,11 +41,14 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
if (!Enabled)
|
if (!Enabled)
|
||||||
throw new ArtemisPluginException(PluginInfo, "Can only add a layer brush descriptor when the plugin is enabled");
|
throw new ArtemisPluginException(PluginInfo, "Can only add a layer brush descriptor when the plugin is enabled");
|
||||||
|
|
||||||
_layerBrushDescriptors.Add(new LayerBrushDescriptor(displayName, description, icon, typeof(T), this));
|
var descriptor = new LayerBrushDescriptor(displayName, description, icon, typeof(T), this);
|
||||||
|
_layerBrushDescriptors.Add(descriptor);
|
||||||
|
LayerBrushStore.Add(descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPluginDisabled(object sender, EventArgs e)
|
private void OnPluginDisabled(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
// The store will clean up the registrations by itself, the plugin just needs to clear its own list
|
||||||
_layerBrushDescriptors.Clear();
|
_layerBrushDescriptors.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,9 +70,9 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Initialize(IRenderElementService renderElementService)
|
internal override void Initialize()
|
||||||
{
|
{
|
||||||
InitializeProperties(renderElementService);
|
InitializeProperties();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,12 +46,12 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
LedGroup.Brush = GetBrush();
|
LedGroup.Brush = GetBrush();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Initialize(IRenderElementService renderElementService)
|
internal override void Initialize()
|
||||||
{
|
{
|
||||||
LedGroup = new ListLedGroup();
|
LedGroup = new ListLedGroup();
|
||||||
Layer.RenderPropertiesUpdated += LayerOnRenderPropertiesUpdated;
|
Layer.RenderPropertiesUpdated += LayerOnRenderPropertiesUpdated;
|
||||||
|
|
||||||
InitializeProperties(renderElementService);
|
InitializeProperties();
|
||||||
UpdateLedGroup();
|
UpdateLedGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -95,7 +95,7 @@ namespace Artemis.Core.LayerEffects
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the plugin info that defined this effect
|
/// Gets the plugin info that defined this effect
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public PluginInfo PluginInfo => Descriptor.LayerEffectProvider.PluginInfo;
|
public PluginInfo PluginInfo => Descriptor.LayerEffectProvider?.PluginInfo;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a reference to the layer property group without knowing it's type
|
/// Gets a reference to the layer property group without knowing it's type
|
||||||
@ -108,7 +108,7 @@ namespace Artemis.Core.LayerEffects
|
|||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
DisableLayerEffect();
|
DisableLayerEffect();
|
||||||
BaseProperties.Dispose();
|
BaseProperties?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -147,6 +147,8 @@ namespace Artemis.Core.LayerEffects
|
|||||||
|
|
||||||
// Not only is this needed to initialize properties on the layer effects, it also prevents implementing anything
|
// Not only is this needed to initialize properties on the layer effects, it also prevents implementing anything
|
||||||
// but LayerEffect<T> outside the core
|
// but LayerEffect<T> outside the core
|
||||||
internal abstract void Initialize(IRenderElementService renderElementService);
|
internal abstract void Initialize();
|
||||||
|
|
||||||
|
internal virtual string GetEffectTypeName() => GetType().Name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -30,19 +30,19 @@ namespace Artemis.Core.LayerEffects
|
|||||||
internal set => _properties = value;
|
internal set => _properties = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void InitializeProperties(IRenderElementService renderElementService)
|
internal void InitializeProperties()
|
||||||
{
|
{
|
||||||
Properties = Activator.CreateInstance<T>();
|
Properties = Activator.CreateInstance<T>();
|
||||||
Properties.LayerEffect = this;
|
Properties.LayerEffect = this;
|
||||||
Properties.InitializeProperties(renderElementService, ProfileElement, PropertyRootPath);
|
Properties.Initialize(ProfileElement, PropertyRootPath, PluginInfo);
|
||||||
PropertiesInitialized = true;
|
PropertiesInitialized = true;
|
||||||
|
|
||||||
EnableLayerEffect();
|
EnableLayerEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Initialize(IRenderElementService renderElementService)
|
internal override void Initialize()
|
||||||
{
|
{
|
||||||
InitializeProperties(renderElementService);
|
InitializeProperties();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,4 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Artemis.Core.LayerEffects.Placeholder;
|
||||||
|
using Artemis.Core.Services;
|
||||||
|
using Artemis.Storage.Entities.Profile;
|
||||||
|
using Ninject;
|
||||||
|
|
||||||
namespace Artemis.Core.LayerEffects
|
namespace Artemis.Core.LayerEffects
|
||||||
{
|
{
|
||||||
@ -41,5 +46,46 @@ namespace Artemis.Core.LayerEffects
|
|||||||
/// The plugin that provided this <see cref="LayerEffectDescriptor" />
|
/// The plugin that provided this <see cref="LayerEffectDescriptor" />
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public LayerEffectProvider LayerEffectProvider { get; }
|
public LayerEffectProvider LayerEffectProvider { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the GUID this descriptor is acting as a placeholder for. If null, this descriptor is not a placeholder
|
||||||
|
/// </summary>
|
||||||
|
public Guid? PlaceholderFor { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an instance of the described effect and applies it to the render element
|
||||||
|
/// </summary>
|
||||||
|
internal void CreateInstance(RenderProfileElement renderElement, LayerEffectEntity entity)
|
||||||
|
{
|
||||||
|
// Skip effects already on the element
|
||||||
|
if (renderElement.LayerEffects.Any(e => e.EntityId == entity.Id))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (PlaceholderFor != null)
|
||||||
|
{
|
||||||
|
CreatePlaceHolderInstance(renderElement, entity);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var effect = (BaseLayerEffect)CoreService.Kernel.Get(LayerEffectType);
|
||||||
|
effect.ProfileElement = renderElement;
|
||||||
|
effect.EntityId = entity.Id;
|
||||||
|
effect.Order = entity.Order;
|
||||||
|
effect.Name = entity.Name;
|
||||||
|
effect.Enabled = entity.Enabled;
|
||||||
|
effect.Descriptor = this;
|
||||||
|
|
||||||
|
effect.Initialize();
|
||||||
|
effect.Update(0);
|
||||||
|
|
||||||
|
renderElement.ActivateLayerEffect(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreatePlaceHolderInstance(RenderProfileElement renderElement, LayerEffectEntity entity)
|
||||||
|
{
|
||||||
|
var effect = new PlaceholderLayerEffect(entity, PlaceholderFor.Value) {ProfileElement = renderElement, Descriptor = this};
|
||||||
|
effect.Initialize();
|
||||||
|
renderElement.ActivateLayerEffect(effect);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,11 +41,14 @@ namespace Artemis.Core.LayerEffects
|
|||||||
if (!Enabled)
|
if (!Enabled)
|
||||||
throw new ArtemisPluginException(PluginInfo, "Can only add a layer effect descriptor when the plugin is enabled");
|
throw new ArtemisPluginException(PluginInfo, "Can only add a layer effect descriptor when the plugin is enabled");
|
||||||
|
|
||||||
_layerEffectDescriptors.Add(new LayerEffectDescriptor(displayName, description, icon, typeof(T), this));
|
var descriptor = new LayerEffectDescriptor(displayName, description, icon, typeof(T), this);
|
||||||
|
_layerEffectDescriptors.Add(descriptor);
|
||||||
|
LayerEffectStore.Add(descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPluginDisabled(object sender, EventArgs e)
|
private void OnPluginDisabled(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
// The store will clean up the registrations by itself, the plugin just needs to clear its own list
|
||||||
_layerEffectDescriptors.Clear();
|
_layerEffectDescriptors.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,75 @@
|
|||||||
|
using System;
|
||||||
|
using Artemis.Storage.Entities.Profile;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.Core.LayerEffects.Placeholder
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a layer effect that could not be loaded due to a missing plugin
|
||||||
|
/// </summary>
|
||||||
|
internal class PlaceholderLayerEffect : LayerEffect<PlaceholderProperties>
|
||||||
|
{
|
||||||
|
internal PlaceholderLayerEffect(LayerEffectEntity originalEntity, Guid placeholderFor)
|
||||||
|
{
|
||||||
|
OriginalEntity = originalEntity;
|
||||||
|
PlaceholderFor = placeholderFor;
|
||||||
|
|
||||||
|
EntityId = OriginalEntity.Id;
|
||||||
|
Order = OriginalEntity.Order;
|
||||||
|
Name = OriginalEntity.Name;
|
||||||
|
Enabled = OriginalEntity.Enabled;
|
||||||
|
HasBeenRenamed = OriginalEntity.HasBeenRenamed;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal LayerEffectEntity OriginalEntity { get; }
|
||||||
|
public Guid PlaceholderFor { 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is in place so that the UI has something to show
|
||||||
|
/// </summary>
|
||||||
|
internal class PlaceholderProperties : LayerPropertyGroup
|
||||||
|
{
|
||||||
|
protected override void PopulateDefaults()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void EnableProperties()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void DisableProperties()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Artemis.Core.LayerEffects.Placeholder
|
||||||
|
{
|
||||||
|
internal static class PlaceholderLayerEffectDescriptor
|
||||||
|
{
|
||||||
|
public static LayerEffectDescriptor Create(Guid missingPluginGuid)
|
||||||
|
{
|
||||||
|
var descriptor = new LayerEffectDescriptor("Missing effect", "This effect could not be loaded", "FileQuestion", null, Constants.EffectPlaceholderPlugin)
|
||||||
|
{
|
||||||
|
PlaceholderFor = missingPluginGuid
|
||||||
|
};
|
||||||
|
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -14,6 +14,7 @@ namespace Artemis.Core.Modules
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The data model driving this module
|
/// The data model driving this module
|
||||||
|
/// <para>Note: This default data model is automatically registered upon plugin enable</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public T DataModel
|
public T DataModel
|
||||||
{
|
{
|
||||||
|
|||||||
@ -18,6 +18,7 @@ namespace Artemis.Core.Modules
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The data model driving this module
|
/// The data model driving this module
|
||||||
|
/// <para>Note: This default data model is automatically registered upon plugin enable</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public T DataModel
|
public T DataModel
|
||||||
{
|
{
|
||||||
@ -118,7 +119,7 @@ namespace Artemis.Core.Modules
|
|||||||
/// Indicates whether or not a profile change is being animated
|
/// Indicates whether or not a profile change is being animated
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AnimatingProfileChange { get; private set; }
|
public bool AnimatingProfileChange { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called after the profile has updated
|
/// Called after the profile has updated
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -16,7 +16,7 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether the plugin is enabled
|
/// Gets whether the plugin is enabled
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Enabled { get; private set; }
|
public bool Enabled { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a configuration dialog for this plugin that is accessible in the UI under Settings > Plugins
|
/// Gets or sets a configuration dialog for this plugin that is accessible in the UI under Settings > Plugins
|
||||||
|
|||||||
@ -9,6 +9,7 @@ using Artemis.Core.JsonConverters;
|
|||||||
using Artemis.Core.Ninject;
|
using Artemis.Core.Ninject;
|
||||||
using Artemis.Storage;
|
using Artemis.Storage;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using Ninject;
|
||||||
using RGB.NET.Core;
|
using RGB.NET.Core;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
@ -22,6 +23,8 @@ namespace Artemis.Core.Services
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal class CoreService : ICoreService
|
internal class CoreService : ICoreService
|
||||||
{
|
{
|
||||||
|
internal static IKernel Kernel;
|
||||||
|
|
||||||
private readonly Stopwatch _frameStopWatch;
|
private readonly Stopwatch _frameStopWatch;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly PluginSetting<LogEventLevel> _loggingLevel;
|
private readonly PluginSetting<LogEventLevel> _loggingLevel;
|
||||||
@ -34,9 +37,10 @@ namespace Artemis.Core.Services
|
|||||||
private List<Module> _modules;
|
private List<Module> _modules;
|
||||||
|
|
||||||
// ReSharper disable once UnusedParameter.Local - Storage migration service is injected early to ensure it runs before anything else
|
// ReSharper disable once UnusedParameter.Local - Storage migration service is injected early to ensure it runs before anything else
|
||||||
public CoreService(ILogger logger, StorageMigrationService _, ISettingsService settingsService, IPluginService pluginService,
|
public CoreService(IKernel kernel, ILogger logger, StorageMigrationService _, ISettingsService settingsService, IPluginService pluginService,
|
||||||
IRgbService rgbService, ISurfaceService surfaceService, IProfileService profileService, IModuleService moduleService)
|
IRgbService rgbService, ISurfaceService surfaceService, IProfileService profileService, IModuleService moduleService)
|
||||||
{
|
{
|
||||||
|
Kernel = kernel;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_pluginService = pluginService;
|
_pluginService = pluginService;
|
||||||
_rgbService = rgbService;
|
_rgbService = rgbService;
|
||||||
@ -77,6 +81,8 @@ namespace Artemis.Core.Services
|
|||||||
_logger.Information("Initializing Artemis Core version {version}", versionAttribute?.InformationalVersion);
|
_logger.Information("Initializing Artemis Core version {version}", versionAttribute?.InformationalVersion);
|
||||||
ApplyLoggingLevel();
|
ApplyLoggingLevel();
|
||||||
|
|
||||||
|
DeserializationLogger.Initialize(Kernel);
|
||||||
|
|
||||||
// Initialize the services
|
// Initialize the services
|
||||||
_pluginService.CopyBuiltInPlugins();
|
_pluginService.CopyBuiltInPlugins();
|
||||||
_pluginService.LoadPlugins(StartupArguments.Contains("--ignore-plugin-lock"));
|
_pluginService.LoadPlugins(StartupArguments.Contains("--ignore-plugin-lock"));
|
||||||
|
|||||||
@ -1,111 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Serilog;
|
|
||||||
|
|
||||||
namespace Artemis.Core.Services
|
|
||||||
{
|
|
||||||
internal class DataBindingService : IDataBindingService
|
|
||||||
{
|
|
||||||
private readonly ILogger _logger;
|
|
||||||
private readonly List<DataBindingModifierType> _registeredDataBindingModifierTypes;
|
|
||||||
|
|
||||||
public DataBindingService(ILogger logger)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_registeredDataBindingModifierTypes = new List<DataBindingModifierType>();
|
|
||||||
|
|
||||||
RegisterBuiltInModifiers();
|
|
||||||
}
|
|
||||||
|
|
||||||
public IReadOnlyCollection<DataBindingModifierType> RegisteredDataBindingModifierTypes
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
lock (_registeredDataBindingModifierTypes)
|
|
||||||
{
|
|
||||||
return _registeredDataBindingModifierTypes.AsReadOnly();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RegisterModifierType(PluginInfo pluginInfo, DataBindingModifierType dataBindingModifierType)
|
|
||||||
{
|
|
||||||
if (pluginInfo == null)
|
|
||||||
throw new ArgumentNullException(nameof(pluginInfo));
|
|
||||||
if (dataBindingModifierType == null)
|
|
||||||
throw new ArgumentNullException(nameof(dataBindingModifierType));
|
|
||||||
|
|
||||||
lock (_registeredDataBindingModifierTypes)
|
|
||||||
{
|
|
||||||
if (_registeredDataBindingModifierTypes.Contains(dataBindingModifierType))
|
|
||||||
return;
|
|
||||||
|
|
||||||
dataBindingModifierType.Register(pluginInfo, this);
|
|
||||||
_registeredDataBindingModifierTypes.Add(dataBindingModifierType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveModifierType(DataBindingModifierType dataBindingModifierType)
|
|
||||||
{
|
|
||||||
if (dataBindingModifierType == null)
|
|
||||||
throw new ArgumentNullException(nameof(dataBindingModifierType));
|
|
||||||
|
|
||||||
lock (_registeredDataBindingModifierTypes)
|
|
||||||
{
|
|
||||||
if (!_registeredDataBindingModifierTypes.Contains(dataBindingModifierType))
|
|
||||||
return;
|
|
||||||
|
|
||||||
dataBindingModifierType.Unsubscribe();
|
|
||||||
_registeredDataBindingModifierTypes.Remove(dataBindingModifierType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<DataBindingModifierType> GetCompatibleModifierTypes(Type type)
|
|
||||||
{
|
|
||||||
lock (_registeredDataBindingModifierTypes)
|
|
||||||
{
|
|
||||||
if (type == null)
|
|
||||||
return new List<DataBindingModifierType>(_registeredDataBindingModifierTypes);
|
|
||||||
|
|
||||||
var candidates = _registeredDataBindingModifierTypes.Where(c => c.CompatibleTypes.Any(t => t.IsCastableFrom(type))).ToList();
|
|
||||||
|
|
||||||
// If there are multiple modifier types with the same description, use the closest match
|
|
||||||
foreach (var dataBindingModifierTypes in candidates.GroupBy(c => c.Description).Where(g => g.Count() > 1).ToList())
|
|
||||||
{
|
|
||||||
var bestCandidate = dataBindingModifierTypes.OrderByDescending(c => c.CompatibleTypes.Contains(type)).FirstOrDefault();
|
|
||||||
foreach (var dataBindingModifierType in dataBindingModifierTypes)
|
|
||||||
{
|
|
||||||
if (dataBindingModifierType != bestCandidate)
|
|
||||||
candidates.Remove(dataBindingModifierType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return candidates;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataBindingModifierType GetModifierType(Guid modifierTypePluginGuid, string modifierType)
|
|
||||||
{
|
|
||||||
return RegisteredDataBindingModifierTypes.FirstOrDefault(o => o.PluginInfo.Guid == modifierTypePluginGuid && o.GetType().Name == modifierType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LogModifierDeserializationFailure(DataBindingModifier dataBindingModifier, JsonSerializationException exception)
|
|
||||||
{
|
|
||||||
_logger.Warning(
|
|
||||||
exception,
|
|
||||||
"Failed to deserialize static parameter for operator {order}. {operatorType}",
|
|
||||||
dataBindingModifier.Entity.Order,
|
|
||||||
dataBindingModifier.Entity.ModifierType
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RegisterBuiltInModifiers()
|
|
||||||
{
|
|
||||||
RegisterModifierType(Constants.CorePluginInfo, new MultiplicationModifierType());
|
|
||||||
RegisterModifierType(Constants.CorePluginInfo, new DivideModifierType());
|
|
||||||
RegisterModifierType(Constants.CorePluginInfo, new FloorModifierType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,252 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Artemis.Core.DataModelExpansions;
|
|
||||||
using Artemis.Core.Modules;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Serilog;
|
|
||||||
|
|
||||||
namespace Artemis.Core.Services
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Provides access to the main data model
|
|
||||||
/// </summary>
|
|
||||||
internal class DataModelService : IDataModelService
|
|
||||||
{
|
|
||||||
private readonly List<DataModel> _dataModelExpansions;
|
|
||||||
private readonly ILogger _logger;
|
|
||||||
private readonly IPluginService _pluginService;
|
|
||||||
private readonly List<DisplayConditionOperator> _registeredConditionOperators;
|
|
||||||
|
|
||||||
public DataModelService(IPluginService pluginService, ILogger logger)
|
|
||||||
{
|
|
||||||
_pluginService = pluginService;
|
|
||||||
_logger = logger;
|
|
||||||
_dataModelExpansions = new List<DataModel>();
|
|
||||||
_registeredConditionOperators = new List<DisplayConditionOperator>();
|
|
||||||
|
|
||||||
_pluginService.PluginEnabled += PluginServiceOnPluginEnabled;
|
|
||||||
_pluginService.PluginDisabled += PluginServiceOnPluginDisabled;
|
|
||||||
|
|
||||||
RegisterBuiltInConditionOperators();
|
|
||||||
|
|
||||||
foreach (var module in _pluginService.GetPluginsOfType<Module>().Where(m => m.InternalExpandsMainDataModel))
|
|
||||||
AddModuleDataModel(module);
|
|
||||||
foreach (var dataModelExpansion in _pluginService.GetPluginsOfType<BaseDataModelExpansion>())
|
|
||||||
AddDataModelExpansionDataModel(dataModelExpansion);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IReadOnlyCollection<DisplayConditionOperator> RegisteredConditionOperators
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
lock (_registeredConditionOperators)
|
|
||||||
{
|
|
||||||
return _registeredConditionOperators.AsReadOnly();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IReadOnlyCollection<DataModel> DataModelExpansions
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
lock (_dataModelExpansions)
|
|
||||||
{
|
|
||||||
return new List<DataModel>(_dataModelExpansions).AsReadOnly();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddExpansion(DataModel dataModelExpansion)
|
|
||||||
{
|
|
||||||
lock (_dataModelExpansions)
|
|
||||||
{
|
|
||||||
_dataModelExpansions.Add(dataModelExpansion);
|
|
||||||
// TODO SpoinkyNL 3-3-2018: Initialize the expansion and fire an event
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveExpansion(DataModel dataModelExpansion)
|
|
||||||
{
|
|
||||||
lock (_dataModelExpansions)
|
|
||||||
{
|
|
||||||
if (!_dataModelExpansions.Contains(dataModelExpansion))
|
|
||||||
throw new ArtemisCoreException("Cannot remove a data model expansion that wasn't previously added.");
|
|
||||||
|
|
||||||
// TODO SpoinkyNL 3-3-2018: Dispose the expansion and fire an event
|
|
||||||
_dataModelExpansions.Remove(dataModelExpansion);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataModel GetPluginDataModel(Plugin plugin)
|
|
||||||
{
|
|
||||||
if (plugin is Module module)
|
|
||||||
return module.InternalDataModel;
|
|
||||||
if (plugin is BaseDataModelExpansion dataModelExpansion)
|
|
||||||
return dataModelExpansion.InternalDataModel;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataModel GetPluginDataModelByGuid(Guid pluginGuid)
|
|
||||||
{
|
|
||||||
var pluginInfo = _pluginService.GetAllPluginInfo().FirstOrDefault(i => i.Guid == pluginGuid);
|
|
||||||
if (pluginInfo == null || !pluginInfo.Enabled)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return GetPluginDataModel(pluginInfo.Instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool GetPluginExtendsDataModel(Plugin plugin)
|
|
||||||
{
|
|
||||||
if (plugin is Module module)
|
|
||||||
return module.InternalExpandsMainDataModel;
|
|
||||||
if (plugin is BaseDataModelExpansion)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void RegisterConditionOperator(PluginInfo pluginInfo, DisplayConditionOperator displayConditionOperator)
|
|
||||||
{
|
|
||||||
if (pluginInfo == null)
|
|
||||||
throw new ArgumentNullException(nameof(pluginInfo));
|
|
||||||
if (displayConditionOperator == null)
|
|
||||||
throw new ArgumentNullException(nameof(displayConditionOperator));
|
|
||||||
|
|
||||||
lock (_registeredConditionOperators)
|
|
||||||
{
|
|
||||||
if (_registeredConditionOperators.Contains(displayConditionOperator))
|
|
||||||
return;
|
|
||||||
|
|
||||||
displayConditionOperator.Register(pluginInfo, this);
|
|
||||||
_registeredConditionOperators.Add(displayConditionOperator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveConditionOperator(DisplayConditionOperator displayConditionOperator)
|
|
||||||
{
|
|
||||||
if (displayConditionOperator == null)
|
|
||||||
throw new ArgumentNullException(nameof(displayConditionOperator));
|
|
||||||
|
|
||||||
lock (_registeredConditionOperators)
|
|
||||||
{
|
|
||||||
if (!_registeredConditionOperators.Contains(displayConditionOperator))
|
|
||||||
return;
|
|
||||||
|
|
||||||
displayConditionOperator.Unsubscribe();
|
|
||||||
_registeredConditionOperators.Remove(displayConditionOperator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<DisplayConditionOperator> GetCompatibleConditionOperators(Type type)
|
|
||||||
{
|
|
||||||
lock (_registeredConditionOperators)
|
|
||||||
{
|
|
||||||
if (type == null)
|
|
||||||
return new List<DisplayConditionOperator>(_registeredConditionOperators);
|
|
||||||
|
|
||||||
var candidates = _registeredConditionOperators.Where(c => c.CompatibleTypes.Any(t => t.IsCastableFrom(type))).ToList();
|
|
||||||
|
|
||||||
// If there are multiple operators with the same description, use the closest match
|
|
||||||
foreach (var displayConditionOperators in candidates.GroupBy(c => c.Description).Where(g => g.Count() > 1).ToList())
|
|
||||||
{
|
|
||||||
var bestCandidate = displayConditionOperators.OrderByDescending(c => c.CompatibleTypes.Contains(type)).FirstOrDefault();
|
|
||||||
foreach (var displayConditionOperator in displayConditionOperators)
|
|
||||||
{
|
|
||||||
if (displayConditionOperator != bestCandidate)
|
|
||||||
candidates.Remove(displayConditionOperator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return candidates;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public DisplayConditionOperator GetConditionOperator(Guid operatorPluginGuid, string operatorType)
|
|
||||||
{
|
|
||||||
return RegisteredConditionOperators.FirstOrDefault(o => o.PluginInfo.Guid == operatorPluginGuid && o.GetType().Name == operatorType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LogPredicateDeserializationFailure(DisplayConditionPredicate displayConditionPredicate, JsonException exception)
|
|
||||||
{
|
|
||||||
_logger.Warning(
|
|
||||||
exception,
|
|
||||||
"Failed to deserialize display condition predicate {left} {operator} {right}",
|
|
||||||
displayConditionPredicate.Entity.LeftPropertyPath,
|
|
||||||
displayConditionPredicate.Entity.OperatorType,
|
|
||||||
displayConditionPredicate.Entity.RightPropertyPath
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LogListPredicateDeserializationFailure(DisplayConditionListPredicate displayConditionPredicate, JsonException exception)
|
|
||||||
{
|
|
||||||
_logger.Warning(
|
|
||||||
exception,
|
|
||||||
"Failed to deserialize display condition list predicate {list} => {left} {operator} {right}",
|
|
||||||
displayConditionPredicate.Entity.ListPropertyPath,
|
|
||||||
displayConditionPredicate.Entity.LeftPropertyPath,
|
|
||||||
displayConditionPredicate.Entity.OperatorType,
|
|
||||||
displayConditionPredicate.Entity.RightPropertyPath
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RegisterBuiltInConditionOperators()
|
|
||||||
{
|
|
||||||
// General usage for any type
|
|
||||||
RegisterConditionOperator(Constants.CorePluginInfo, new EqualsConditionOperator());
|
|
||||||
RegisterConditionOperator(Constants.CorePluginInfo, new NotEqualConditionOperator());
|
|
||||||
|
|
||||||
// Numeric operators
|
|
||||||
RegisterConditionOperator(Constants.CorePluginInfo, new LessThanConditionOperator());
|
|
||||||
RegisterConditionOperator(Constants.CorePluginInfo, new GreaterThanConditionOperator());
|
|
||||||
RegisterConditionOperator(Constants.CorePluginInfo, new LessThanOrEqualConditionOperator());
|
|
||||||
RegisterConditionOperator(Constants.CorePluginInfo, new GreaterThanOrEqualConditionOperator());
|
|
||||||
|
|
||||||
// String operators
|
|
||||||
RegisterConditionOperator(Constants.CorePluginInfo, new StringEqualsConditionOperator());
|
|
||||||
RegisterConditionOperator(Constants.CorePluginInfo, new StringNotEqualConditionOperator());
|
|
||||||
RegisterConditionOperator(Constants.CorePluginInfo, new StringContainsConditionOperator());
|
|
||||||
RegisterConditionOperator(Constants.CorePluginInfo, new StringNotContainsConditionOperator());
|
|
||||||
RegisterConditionOperator(Constants.CorePluginInfo, new StringStartsWithConditionOperator());
|
|
||||||
RegisterConditionOperator(Constants.CorePluginInfo, new StringEndsWithConditionOperator());
|
|
||||||
RegisterConditionOperator(Constants.CorePluginInfo, new StringNullConditionOperator());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PluginServiceOnPluginEnabled(object sender, PluginEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.PluginInfo.Instance is Module module && module.InternalExpandsMainDataModel)
|
|
||||||
AddModuleDataModel(module);
|
|
||||||
else if (e.PluginInfo.Instance is BaseDataModelExpansion dataModelExpansion)
|
|
||||||
AddDataModelExpansionDataModel(dataModelExpansion);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddDataModelExpansionDataModel(BaseDataModelExpansion dataModelExpansion)
|
|
||||||
{
|
|
||||||
if (dataModelExpansion.InternalDataModel.DataModelDescription == null)
|
|
||||||
throw new ArtemisPluginException(dataModelExpansion.PluginInfo, "Data model expansion overrides GetDataModelDescription but returned null");
|
|
||||||
|
|
||||||
AddExpansion(dataModelExpansion.InternalDataModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddModuleDataModel(Module module)
|
|
||||||
{
|
|
||||||
if (module.InternalDataModel.DataModelDescription == null)
|
|
||||||
throw new ArtemisPluginException(module.PluginInfo, "Module overrides GetDataModelDescription but returned null");
|
|
||||||
|
|
||||||
AddExpansion(module.InternalDataModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PluginServiceOnPluginDisabled(object sender, PluginEventArgs e)
|
|
||||||
{
|
|
||||||
// Remove all data models related to the plugin
|
|
||||||
lock (_dataModelExpansions)
|
|
||||||
{
|
|
||||||
var toRemove = _dataModelExpansions.Where(d => d.PluginInfo == e.PluginInfo).ToList();
|
|
||||||
foreach (var dataModel in toRemove)
|
|
||||||
_dataModelExpansions.Remove(dataModel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -3,6 +3,9 @@ using System.Collections.Generic;
|
|||||||
|
|
||||||
namespace Artemis.Core.Services
|
namespace Artemis.Core.Services
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A service that initializes the Core and manages the render loop
|
||||||
|
/// </summary>
|
||||||
public interface ICoreService : IArtemisService, IDisposable
|
public interface ICoreService : IArtemisService, IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -1,91 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Artemis.Core.DataModelExpansions;
|
|
||||||
using Artemis.Core.Properties;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Artemis.Core.Services
|
|
||||||
{
|
|
||||||
public interface IDataModelService : IArtemisService
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a read-only collection of all registered condition operators
|
|
||||||
/// </summary>
|
|
||||||
IReadOnlyCollection<DisplayConditionOperator> RegisteredConditionOperators { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a read-only collection of all registered data model expansions
|
|
||||||
/// </summary>
|
|
||||||
IReadOnlyCollection<DataModel> DataModelExpansions { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Add an expansion to the datamodel to be available for use after the next update
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="baseDataModelExpansion"></param>
|
|
||||||
void AddExpansion(DataModel baseDataModelExpansion);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Remove a previously added expansion so that it is no longer available and updated
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="baseDataModelExpansion"></param>
|
|
||||||
void RemoveExpansion(DataModel baseDataModelExpansion);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If found, returns the data model of the provided plugin
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="plugin">Should be a module with a data model or a data model expansion</param>
|
|
||||||
DataModel GetPluginDataModel(Plugin plugin);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If found, returns the data model of the provided plugin
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pluginGuid">Should be a module with a data model or a data model expansion</param>
|
|
||||||
DataModel GetPluginDataModelByGuid(Guid pluginGuid);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the given plugin expands the main data model
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="plugin"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
bool GetPluginExtendsDataModel(Plugin plugin);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Registers a new condition operator for use in layer conditions
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pluginInfo">The PluginInfo of the plugin this condition operator belongs to</param>
|
|
||||||
/// <param name="displayConditionOperator">The condition operator to register</param>
|
|
||||||
void RegisterConditionOperator([NotNull] PluginInfo pluginInfo, [NotNull] DisplayConditionOperator displayConditionOperator);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes a condition operator so it is no longer available for use in layer conditions
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="displayConditionOperator">The layer condition operator to remove</param>
|
|
||||||
void RemoveConditionOperator([NotNull] DisplayConditionOperator displayConditionOperator);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns all the display condition operators compatible with the provided type
|
|
||||||
/// </summary>
|
|
||||||
List<DisplayConditionOperator> GetCompatibleConditionOperators(Type type);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a condition operator by its plugin GUID and type name
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="operatorPluginGuid">The operator's plugin GUID</param>
|
|
||||||
/// <param name="operatorType">The type name of the operator</param>
|
|
||||||
DisplayConditionOperator GetConditionOperator(Guid operatorPluginGuid, string operatorType);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Logs a predicate deserialization failure
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="displayConditionPredicate">The predicate that failed to deserialize</param>
|
|
||||||
/// <param name="exception">The JSON exception that occurred</param>
|
|
||||||
void LogPredicateDeserializationFailure(DisplayConditionPredicate displayConditionPredicate, JsonException exception);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Logs a list predicate deserialization failure
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="displayConditionListPredicate">The list predicate that failed to deserialize</param>
|
|
||||||
/// <param name="exception">The JSON exception that occurred</param>
|
|
||||||
void LogListPredicateDeserializationFailure(DisplayConditionListPredicate displayConditionListPredicate, JsonException exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,5 +1,8 @@
|
|||||||
namespace Artemis.Core.Services
|
namespace Artemis.Core.Services
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A service that allows you manage an <see cref="ArtemisDevice"/>
|
||||||
|
/// </summary>
|
||||||
public interface IDeviceService : IArtemisService
|
public interface IDeviceService : IArtemisService
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -1,59 +0,0 @@
|
|||||||
using Artemis.Core.LayerBrushes;
|
|
||||||
using Artemis.Core.LayerEffects;
|
|
||||||
|
|
||||||
namespace Artemis.Core.Services
|
|
||||||
{
|
|
||||||
public interface IRenderElementService : IArtemisService
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new layer
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="profile"></param>
|
|
||||||
/// <param name="parent"></param>
|
|
||||||
/// <param name="name"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Layer CreateLayer(Profile profile, ProfileElement parent, string name);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes the currently active layer brush from the <see cref="Layer" /> and deletes any settings
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="layer">The layer to remove the active brush from</param>
|
|
||||||
void RemoveLayerBrush(Layer layer);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Deactivates the currently active layer brush from the <see cref="Layer" /> but keeps all settings
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="layer">The layer to deactivate the active brush on</param>
|
|
||||||
void DeactivateLayerBrush(Layer layer);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Instantiates and adds the <see cref="BaseLayerBrush" /> described by the provided
|
|
||||||
/// <see cref="LayerBrushDescriptor" />
|
|
||||||
/// to the <see cref="Layer" />.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="layer">The layer to instantiate the brush for</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
BaseLayerBrush InstantiateLayerBrush(Layer layer);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Instantiates and adds the <see cref="BaseLayerEffect" /> described by the provided
|
|
||||||
/// <see cref="LayerEffectDescriptor" /> to the <see cref="Layer" />.
|
|
||||||
/// </summary>
|
|
||||||
/// <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="renderProfileElement">The layer/folder to instantiate the effect for</param>
|
|
||||||
/// <param name="layerEffectDescriptor"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
BaseLayerEffect AddLayerEffect(RenderProfileElement renderProfileElement, LayerEffectDescriptor layerEffectDescriptor);
|
|
||||||
|
|
||||||
void RemoveLayerEffect(BaseLayerEffect layerEffect);
|
|
||||||
|
|
||||||
void InstantiateDisplayConditions(RenderProfileElement renderElement);
|
|
||||||
void InstantiateDataBindings(RenderProfileElement renderElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -4,7 +4,10 @@ using RGB.NET.Core;
|
|||||||
|
|
||||||
namespace Artemis.Core.Services
|
namespace Artemis.Core.Services
|
||||||
{
|
{
|
||||||
public interface IRgbService : IArtemisService
|
/// <summary>
|
||||||
|
/// A service that allows you to manage the <see cref="RGBSurface" /> and its contents
|
||||||
|
/// </summary>
|
||||||
|
public interface IRgbService : IArtemisService, IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the RGB surface rendering is performed on
|
/// Gets or sets the RGB surface rendering is performed on
|
||||||
@ -26,7 +29,14 @@ namespace Artemis.Core.Services
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
IReadOnlyCollection<IRGBDevice> LoadedDevices { get; }
|
IReadOnlyCollection<IRGBDevice> LoadedDevices { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the update trigger that drives the render loop
|
||||||
|
/// </summary>
|
||||||
TimerUpdateTrigger UpdateTrigger { get; }
|
TimerUpdateTrigger UpdateTrigger { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether rendering should be paused
|
||||||
|
/// </summary>
|
||||||
bool IsRenderPaused { get; set; }
|
bool IsRenderPaused { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -35,8 +45,6 @@ namespace Artemis.Core.Services
|
|||||||
/// <param name="deviceProvider"></param>
|
/// <param name="deviceProvider"></param>
|
||||||
void AddDeviceProvider(IRGBDeviceProvider deviceProvider);
|
void AddDeviceProvider(IRGBDeviceProvider deviceProvider);
|
||||||
|
|
||||||
void Dispose();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when a single device has loaded
|
/// Occurs when a single device has loaded
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -47,6 +55,9 @@ namespace Artemis.Core.Services
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
event EventHandler<DeviceEventArgs> DeviceReloaded;
|
event EventHandler<DeviceEventArgs> DeviceReloaded;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Recalculates the LED group used by the <see cref="BitmapBrush" />
|
||||||
|
/// </summary>
|
||||||
void UpdateSurfaceLedGroup();
|
void UpdateSurfaceLedGroup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Services
|
||||||
|
{
|
||||||
|
internal class ConditionOperatorService : IConditionOperatorService
|
||||||
|
{
|
||||||
|
public ConditionOperatorService()
|
||||||
|
{
|
||||||
|
RegisterBuiltInConditionOperators();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConditionOperatorRegistration RegisterConditionOperator(PluginInfo pluginInfo, ConditionOperator conditionOperator)
|
||||||
|
{
|
||||||
|
if (pluginInfo == null)
|
||||||
|
throw new ArgumentNullException(nameof(pluginInfo));
|
||||||
|
if (conditionOperator == null)
|
||||||
|
throw new ArgumentNullException(nameof(conditionOperator));
|
||||||
|
|
||||||
|
conditionOperator.PluginInfo = pluginInfo;
|
||||||
|
return ConditionOperatorStore.Add(conditionOperator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveConditionOperator(ConditionOperatorRegistration registration)
|
||||||
|
{
|
||||||
|
if (registration == null)
|
||||||
|
throw new ArgumentNullException(nameof(registration));
|
||||||
|
ConditionOperatorStore.Remove(registration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ConditionOperator> GetConditionOperatorsForType(Type type)
|
||||||
|
{
|
||||||
|
return ConditionOperatorStore.GetForType(type).Select(r => r.ConditionOperator).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConditionOperator GetConditionOperator(Guid operatorPluginGuid, string operatorType)
|
||||||
|
{
|
||||||
|
return ConditionOperatorStore.Get(operatorPluginGuid, operatorType)?.ConditionOperator;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterBuiltInConditionOperators()
|
||||||
|
{
|
||||||
|
// General usage for any type
|
||||||
|
RegisterConditionOperator(Constants.CorePluginInfo, new EqualsConditionOperator());
|
||||||
|
RegisterConditionOperator(Constants.CorePluginInfo, new NotEqualConditionOperator());
|
||||||
|
|
||||||
|
// Numeric operators
|
||||||
|
RegisterConditionOperator(Constants.CorePluginInfo, new LessThanConditionOperator());
|
||||||
|
RegisterConditionOperator(Constants.CorePluginInfo, new GreaterThanConditionOperator());
|
||||||
|
RegisterConditionOperator(Constants.CorePluginInfo, new LessThanOrEqualConditionOperator());
|
||||||
|
RegisterConditionOperator(Constants.CorePluginInfo, new GreaterThanOrEqualConditionOperator());
|
||||||
|
|
||||||
|
// String operators
|
||||||
|
RegisterConditionOperator(Constants.CorePluginInfo, new StringEqualsConditionOperator());
|
||||||
|
RegisterConditionOperator(Constants.CorePluginInfo, new StringNotEqualConditionOperator());
|
||||||
|
RegisterConditionOperator(Constants.CorePluginInfo, new StringContainsConditionOperator());
|
||||||
|
RegisterConditionOperator(Constants.CorePluginInfo, new StringNotContainsConditionOperator());
|
||||||
|
RegisterConditionOperator(Constants.CorePluginInfo, new StringStartsWithConditionOperator());
|
||||||
|
RegisterConditionOperator(Constants.CorePluginInfo, new StringEndsWithConditionOperator());
|
||||||
|
RegisterConditionOperator(Constants.CorePluginInfo, new StringNullConditionOperator());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
49
src/Artemis.Core/Services/Registration/DataBindingService.cs
Normal file
49
src/Artemis.Core/Services/Registration/DataBindingService.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Services
|
||||||
|
{
|
||||||
|
internal class DataBindingService : IDataBindingService
|
||||||
|
{
|
||||||
|
public DataBindingService()
|
||||||
|
{
|
||||||
|
RegisterBuiltInModifiers();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataBindingModifierTypeRegistration RegisterModifierType(PluginInfo pluginInfo, DataBindingModifierType dataBindingModifierType)
|
||||||
|
{
|
||||||
|
if (pluginInfo == null)
|
||||||
|
throw new ArgumentNullException(nameof(pluginInfo));
|
||||||
|
if (dataBindingModifierType == null)
|
||||||
|
throw new ArgumentNullException(nameof(dataBindingModifierType));
|
||||||
|
|
||||||
|
dataBindingModifierType.PluginInfo = pluginInfo;
|
||||||
|
return DataBindingModifierTypeStore.Add(dataBindingModifierType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveModifierType(DataBindingModifierTypeRegistration registration)
|
||||||
|
{
|
||||||
|
if (registration == null)
|
||||||
|
throw new ArgumentNullException(nameof(registration));
|
||||||
|
DataBindingModifierTypeStore.Remove(registration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DataBindingModifierType> GetCompatibleModifierTypes(Type type)
|
||||||
|
{
|
||||||
|
return DataBindingModifierTypeStore.GetForType(type).Select(r => r.DataBindingModifierType).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataBindingModifierType GetModifierType(Guid modifierTypePluginGuid, string modifierType)
|
||||||
|
{
|
||||||
|
return DataBindingModifierTypeStore.Get(modifierTypePluginGuid, modifierType)?.DataBindingModifierType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterBuiltInModifiers()
|
||||||
|
{
|
||||||
|
RegisterModifierType(Constants.CorePluginInfo, new MultiplicationModifierType());
|
||||||
|
RegisterModifierType(Constants.CorePluginInfo, new DivideModifierType());
|
||||||
|
RegisterModifierType(Constants.CorePluginInfo, new FloorModifierType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
86
src/Artemis.Core/Services/Registration/DataModelService.cs
Normal file
86
src/Artemis.Core/Services/Registration/DataModelService.cs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Artemis.Core.DataModelExpansions;
|
||||||
|
using Artemis.Core.Modules;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Services
|
||||||
|
{
|
||||||
|
internal class DataModelService : IDataModelService
|
||||||
|
{
|
||||||
|
public DataModelService(IPluginService pluginService)
|
||||||
|
{
|
||||||
|
// Add data models of already loaded plugins
|
||||||
|
foreach (var module in pluginService.GetPluginsOfType<Module>())
|
||||||
|
AddModuleDataModel(module);
|
||||||
|
foreach (var dataModelExpansion in pluginService.GetPluginsOfType<BaseDataModelExpansion>())
|
||||||
|
AddDataModelExpansionDataModel(dataModelExpansion);
|
||||||
|
|
||||||
|
// Add data models of new plugins when they get enabled
|
||||||
|
pluginService.PluginEnabled += PluginServiceOnPluginEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataModelRegistration RegisterDataModel(DataModel dataModel)
|
||||||
|
{
|
||||||
|
if (dataModel == null)
|
||||||
|
throw new ArgumentNullException(nameof(dataModel));
|
||||||
|
return DataModelStore.Add(dataModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveDataModel(DataModelRegistration registration)
|
||||||
|
{
|
||||||
|
if (registration == null)
|
||||||
|
throw new ArgumentNullException(nameof(registration));
|
||||||
|
DataModelStore.Remove(registration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DataModel> GetDataModels()
|
||||||
|
{
|
||||||
|
return DataModelStore.GetAll().Select(d => d.DataModel).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T GetDataModel<T>() where T : DataModel
|
||||||
|
{
|
||||||
|
return (T) DataModelStore.GetAll().FirstOrDefault(d => d.DataModel is T)?.DataModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataModel GetPluginDataModel(Plugin plugin)
|
||||||
|
{
|
||||||
|
return DataModelStore.Get(plugin.PluginInfo.Guid)?.DataModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataModel GetPluginDataModel(Guid pluginGuid)
|
||||||
|
{
|
||||||
|
return DataModelStore.Get(pluginGuid)?.DataModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PluginServiceOnPluginEnabled(object sender, PluginEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.PluginInfo.Instance is Module module)
|
||||||
|
AddModuleDataModel(module);
|
||||||
|
else if (e.PluginInfo.Instance is BaseDataModelExpansion dataModelExpansion)
|
||||||
|
AddDataModelExpansionDataModel(dataModelExpansion);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddModuleDataModel(Module module)
|
||||||
|
{
|
||||||
|
if (module.InternalDataModel == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (module.InternalDataModel.DataModelDescription == null)
|
||||||
|
throw new ArtemisPluginException(module.PluginInfo, "Module overrides GetDataModelDescription but returned null");
|
||||||
|
|
||||||
|
module.InternalDataModel.IsExpansion = module.InternalExpandsMainDataModel;
|
||||||
|
RegisterDataModel(module.InternalDataModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddDataModelExpansionDataModel(BaseDataModelExpansion dataModelExpansion)
|
||||||
|
{
|
||||||
|
if (dataModelExpansion.InternalDataModel.DataModelDescription == null)
|
||||||
|
throw new ArtemisPluginException(dataModelExpansion.PluginInfo, "Data model expansion overrides GetDataModelDescription but returned null");
|
||||||
|
|
||||||
|
dataModelExpansion.InternalDataModel.IsExpansion = true;
|
||||||
|
RegisterDataModel(dataModelExpansion.InternalDataModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Artemis.Core.Properties;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A service that allows you to register and retrieve conditions operators used by display conditions
|
||||||
|
/// </summary>
|
||||||
|
public interface IConditionOperatorService : IArtemisService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a new condition operator for use in layer conditions
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pluginInfo">The PluginInfo of the plugin this condition operator belongs to</param>
|
||||||
|
/// <param name="conditionOperator">The condition operator to register</param>
|
||||||
|
ConditionOperatorRegistration RegisterConditionOperator([NotNull] PluginInfo pluginInfo, [NotNull] ConditionOperator conditionOperator);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a condition operator so it is no longer available for use in layer conditions
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="registration">The registration of the condition operator to remove</param>
|
||||||
|
void RemoveConditionOperator([NotNull] ConditionOperatorRegistration registration);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns all the condition operators compatible with the provided type
|
||||||
|
/// </summary>
|
||||||
|
List<ConditionOperator> GetConditionOperatorsForType(Type type);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a condition operator by its plugin GUID and type name
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="operatorPluginGuid">The operator's plugin GUID</param>
|
||||||
|
/// <param name="operatorType">The type name of the operator</param>
|
||||||
|
ConditionOperator GetConditionOperator(Guid operatorPluginGuid, string operatorType);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,29 +1,26 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Artemis.Core.Properties;
|
using Artemis.Core.Properties;
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Artemis.Core.Services
|
namespace Artemis.Core.Services
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A service that allows you to register and retrieve data binding modifiers used by the data bindings system
|
||||||
|
/// </summary>
|
||||||
public interface IDataBindingService : IArtemisService
|
public interface IDataBindingService : IArtemisService
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Gets a read-only collection of all registered modifier types
|
|
||||||
/// </summary>
|
|
||||||
IReadOnlyCollection<DataBindingModifierType> RegisteredDataBindingModifierTypes { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers a new modifier type for use in data bindings
|
/// Registers a new modifier type for use in data bindings
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pluginInfo">The PluginInfo of the plugin this modifier type belongs to</param>
|
/// <param name="pluginInfo">The PluginInfo of the plugin this modifier type belongs to</param>
|
||||||
/// <param name="dataBindingModifierType">The modifier type to register</param>
|
/// <param name="dataBindingModifierType">The modifier type to register</param>
|
||||||
void RegisterModifierType([NotNull] PluginInfo pluginInfo, [NotNull] DataBindingModifierType dataBindingModifierType);
|
DataBindingModifierTypeRegistration RegisterModifierType([NotNull] PluginInfo pluginInfo, [NotNull] DataBindingModifierType dataBindingModifierType);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes a modifier type so it is no longer available for use in data bindings
|
/// Removes a modifier type so it is no longer available for use in data bindings
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dataBindingModifierType">The modifier type to remove</param>
|
/// <param name="dataBindingModifierType">The registration of the modifier type to remove</param>
|
||||||
void RemoveModifierType([NotNull] DataBindingModifierType dataBindingModifierType);
|
void RemoveModifierType([NotNull] DataBindingModifierTypeRegistration dataBindingModifierType);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns all the data binding modifier types compatible with the provided type
|
/// Returns all the data binding modifier types compatible with the provided type
|
||||||
@ -37,12 +34,5 @@ namespace Artemis.Core.Services
|
|||||||
/// <param name="modifierType">The type name of the modifier type</param>
|
/// <param name="modifierType">The type name of the modifier type</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
DataBindingModifierType GetModifierType(Guid modifierTypePluginGuid, string modifierType);
|
DataBindingModifierType GetModifierType(Guid modifierTypePluginGuid, string modifierType);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Logs a modifier deserialization failure
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dataBindingModifier">The modifier that failed to deserialize</param>
|
|
||||||
/// <param name="exception">The JSON exception that occurred</param>
|
|
||||||
void LogModifierDeserializationFailure(DataBindingModifier dataBindingModifier, JsonSerializationException exception);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Artemis.Core.DataModelExpansions;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A service that allows you to register and retrieve data models
|
||||||
|
/// </summary>
|
||||||
|
public interface IDataModelService : IArtemisService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Add a data model to so that it is available to conditions and data bindings
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dataModel"></param>
|
||||||
|
DataModelRegistration RegisterDataModel(DataModel dataModel);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove a previously added data model so that it is no longer available
|
||||||
|
/// </summary>
|
||||||
|
void RemoveDataModel(DataModelRegistration registration);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a list of all registered data models
|
||||||
|
/// </summary>
|
||||||
|
List<DataModel> GetDataModels();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If found, returns the registered data model of type <typeparamref name="T" />
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the data model to find</typeparam>
|
||||||
|
T GetDataModel<T>() where T : DataModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If found, returns the data model of the provided plugin
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="plugin">The plugin to find the data model of</param>
|
||||||
|
DataModel GetPluginDataModel(Plugin plugin);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If found, returns the data model of the provided plugin GUID
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pluginGuid">The GUID of the plugin to find the data model of</param>
|
||||||
|
DataModel GetPluginDataModel(Guid pluginGuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Artemis.Core.LayerBrushes;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A service that allows you to register and retrieve layer brushes
|
||||||
|
/// </summary>
|
||||||
|
public interface ILayerBrushService : IArtemisService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Add a layer brush descriptor so that it is available to layers
|
||||||
|
/// </summary>
|
||||||
|
LayerBrushRegistration RegisterLayerBrush(LayerBrushDescriptor descriptor);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove a previously added layer brush descriptor so that it is no longer available
|
||||||
|
/// </summary>
|
||||||
|
void RemoveLayerBrush(LayerBrushRegistration registration);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a list of all registered layer brush descriptors
|
||||||
|
/// </summary>
|
||||||
|
List<LayerBrushDescriptor> GetLayerBrushes();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the descriptor of the default layer brush
|
||||||
|
/// </summary>
|
||||||
|
LayerBrushDescriptor GetDefaultLayerBrush();
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user