mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Device visualizer - Fixed memory leak
Device visualizer - Improved performance on very large devices Dynamic data model - Reworked API
This commit is contained in:
parent
8cba36f5d4
commit
6904a137e1
@ -8,7 +8,7 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class DynamicDataModelChildEventArgs : EventArgs
|
public class DynamicDataModelChildEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
internal DynamicDataModelChildEventArgs(object? dynamicChild, string key)
|
internal DynamicDataModelChildEventArgs(DynamicChild dynamicChild, string key)
|
||||||
{
|
{
|
||||||
DynamicChild = dynamicChild;
|
DynamicChild = dynamicChild;
|
||||||
Key = key;
|
Key = key;
|
||||||
@ -17,7 +17,7 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the dynamic data model child
|
/// Gets the dynamic data model child
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public object? DynamicChild { get; }
|
public DynamicChild DynamicChild { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the key of the dynamic data model on the parent <see cref="DataModel" />
|
/// Gets the key of the dynamic data model on the parent <see cref="DataModel" />
|
||||||
|
|||||||
@ -15,8 +15,8 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
private Expression<Func<object, object>>? _accessorLambda;
|
private Expression<Func<object, object>>? _accessorLambda;
|
||||||
private DataModel? _dynamicDataModel;
|
private DataModel? _dynamicDataModel;
|
||||||
private Type _dynamicDataModelType;
|
private Type? _dynamicDataModelType;
|
||||||
private DataModelPropertyAttribute _dynamicDataModelAttribute;
|
private DataModelPropertyAttribute? _dynamicDataModelAttribute;
|
||||||
|
|
||||||
internal DataModelPathSegment(DataModelPath dataModelPath, string identifier, string path)
|
internal DataModelPathSegment(DataModelPath dataModelPath, string identifier, string path)
|
||||||
{
|
{
|
||||||
@ -184,8 +184,8 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
// If a dynamic data model is found the use that
|
// If a dynamic data model is found the use that
|
||||||
bool hasDynamicChild = _dynamicDataModel.DynamicChildren.TryGetValue(Identifier, out DynamicChild? dynamicChild);
|
bool hasDynamicChild = _dynamicDataModel.DynamicChildren.TryGetValue(Identifier, out DynamicChild? dynamicChild);
|
||||||
if (hasDynamicChild && dynamicChild?.Value != null)
|
if (hasDynamicChild && dynamicChild?.BaseValue != null)
|
||||||
DetermineDynamicType(dynamicChild.Value, dynamicChild.Attribute);
|
DetermineDynamicType(dynamicChild.BaseValue, dynamicChild.Attribute);
|
||||||
|
|
||||||
_dynamicDataModel.DynamicChildAdded += DynamicChildOnDynamicChildAdded;
|
_dynamicDataModel.DynamicChildAdded += DynamicChildOnDynamicChildAdded;
|
||||||
_dynamicDataModel.DynamicChildRemoved += DynamicChildOnDynamicChildRemoved;
|
_dynamicDataModel.DynamicChildRemoved += DynamicChildOnDynamicChildRemoved;
|
||||||
@ -212,12 +212,14 @@ namespace Artemis.Core
|
|||||||
accessorExpression = Expression.PropertyOrField(expression, Identifier);
|
accessorExpression = Expression.PropertyOrField(expression, Identifier);
|
||||||
// A dynamic segment calls the generic method DataModel.DynamicChild<T> and provides the identifier as an argument
|
// A dynamic segment calls the generic method DataModel.DynamicChild<T> and provides the identifier as an argument
|
||||||
else
|
else
|
||||||
|
{
|
||||||
accessorExpression = Expression.Call(
|
accessorExpression = Expression.Call(
|
||||||
expression,
|
expression,
|
||||||
nameof(DataModel.DynamicChild),
|
nameof(DataModel.GetDynamicChildValue),
|
||||||
_dynamicDataModelType != null ? new[] { _dynamicDataModelType } : null,
|
_dynamicDataModelType != null ? new[] { _dynamicDataModelType } : null,
|
||||||
Expression.Constant(Identifier)
|
Expression.Constant(Identifier)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
_accessorLambda = Expression.Lambda<Func<object, object>>(
|
_accessorLambda = Expression.Lambda<Func<object, object>>(
|
||||||
// Wrap with a null check
|
// Wrap with a null check
|
||||||
@ -290,7 +292,7 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
private void DynamicChildOnDynamicChildRemoved(object? sender, DynamicDataModelChildEventArgs e)
|
private void DynamicChildOnDynamicChildRemoved(object? sender, DynamicDataModelChildEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.DynamicChild == _dynamicDataModel)
|
if (e.DynamicChild.BaseValue == _dynamicDataModel)
|
||||||
DataModelPath.Initialize();
|
DataModelPath.Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Artemis.Core.Modules;
|
using Artemis.Core.Modules;
|
||||||
@ -66,183 +67,6 @@ namespace Artemis.Core.DataModelExpansions
|
|||||||
return new List<PropertyInfo>().AsReadOnly();
|
return new List<PropertyInfo>().AsReadOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a dynamic child to this data model
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dynamicChild">The dynamic child to add</param>
|
|
||||||
/// <param name="key">The key of the child, must be unique to this data model</param>
|
|
||||||
/// <param name="name">An optional human readable name, if not provided the key will be used in a humanized form</param>
|
|
||||||
public T AddDynamicChild<T>(T dynamicChild, string key, string? name = null)
|
|
||||||
{
|
|
||||||
if (dynamicChild == null)
|
|
||||||
throw new ArgumentNullException(nameof(dynamicChild));
|
|
||||||
if (key == null)
|
|
||||||
throw new ArgumentNullException(nameof(key));
|
|
||||||
if (key.Contains('.'))
|
|
||||||
throw new ArtemisCoreException("The provided key contains an illegal character (.)");
|
|
||||||
if (_dynamicChildren.ContainsKey(key))
|
|
||||||
{
|
|
||||||
throw new ArtemisCoreException($"Cannot add a dynamic child with key '{key}' " +
|
|
||||||
"because the key is already in use on by another dynamic property this data model.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GetType().GetProperty(key) != null)
|
|
||||||
{
|
|
||||||
throw new ArtemisCoreException($"Cannot add a dynamic child with key '{key}' " +
|
|
||||||
"because the key is already in use by a static property on this data model.");
|
|
||||||
}
|
|
||||||
|
|
||||||
DataModelPropertyAttribute attribute = new()
|
|
||||||
{
|
|
||||||
Name = string.IsNullOrWhiteSpace(name) ? key.Humanize() : name
|
|
||||||
};
|
|
||||||
if (dynamicChild is DataModel dynamicDataModel)
|
|
||||||
{
|
|
||||||
dynamicDataModel.Feature = Feature;
|
|
||||||
dynamicDataModel.DataModelDescription = attribute;
|
|
||||||
}
|
|
||||||
|
|
||||||
_dynamicChildren.Add(key, new DynamicChild(attribute, dynamicChild));
|
|
||||||
|
|
||||||
OnDynamicDataModelAdded(new DynamicDataModelChildEventArgs(dynamicChild, key));
|
|
||||||
return dynamicChild;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a dynamic child to this data model
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dynamicChild">The dynamic child to add</param>
|
|
||||||
/// <param name="key">The key of the child, must be unique to this data model</param>
|
|
||||||
/// <param name="name">A human readable for your dynamic child, shown in the UI</param>
|
|
||||||
/// <param name="description">An optional description, shown in the UI</param>
|
|
||||||
public T AddDynamicChild<T>(T dynamicChild, string key, string name, string description)
|
|
||||||
{
|
|
||||||
if (dynamicChild == null)
|
|
||||||
throw new ArgumentNullException(nameof(dynamicChild));
|
|
||||||
if (key == null)
|
|
||||||
throw new ArgumentNullException(nameof(key));
|
|
||||||
if (key.Contains('.'))
|
|
||||||
throw new ArtemisCoreException("The provided key contains an illegal character (.)");
|
|
||||||
if (_dynamicChildren.ContainsKey(key))
|
|
||||||
{
|
|
||||||
throw new ArtemisCoreException($"Cannot add a dynamic child with key '{key}' " +
|
|
||||||
"because the key is already in use on by another dynamic property this data model.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GetType().GetProperty(key) != null)
|
|
||||||
{
|
|
||||||
throw new ArtemisCoreException($"Cannot add a dynamic child with key '{key}' " +
|
|
||||||
"because the key is already in use by a static property on this data model.");
|
|
||||||
}
|
|
||||||
|
|
||||||
DataModelPropertyAttribute attribute = new()
|
|
||||||
{
|
|
||||||
Name = string.IsNullOrWhiteSpace(name) ? key.Humanize() : name,
|
|
||||||
Description = description
|
|
||||||
};
|
|
||||||
if (dynamicChild is DataModel dynamicDataModel)
|
|
||||||
{
|
|
||||||
dynamicDataModel.Feature = Feature;
|
|
||||||
dynamicDataModel.DataModelDescription = attribute;
|
|
||||||
}
|
|
||||||
|
|
||||||
_dynamicChildren.Add(key, new DynamicChild(attribute, dynamicChild));
|
|
||||||
|
|
||||||
OnDynamicDataModelAdded(new DynamicDataModelChildEventArgs(dynamicChild, key));
|
|
||||||
return dynamicChild;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a dynamic child to this data model
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dynamicChild">The dynamic child to add</param>
|
|
||||||
/// <param name="key">The key of the child, must be unique to this data model</param>
|
|
||||||
/// <param name="attribute">A data model property attribute describing the dynamic child</param>
|
|
||||||
public T AddDynamicChild<T>(T dynamicChild, string key, DataModelPropertyAttribute attribute)
|
|
||||||
{
|
|
||||||
if (dynamicChild == null) throw new ArgumentNullException(nameof(dynamicChild));
|
|
||||||
if (key == null) throw new ArgumentNullException(nameof(key));
|
|
||||||
if (attribute == null) throw new ArgumentNullException(nameof(attribute));
|
|
||||||
if (key.Contains('.'))
|
|
||||||
throw new ArtemisCoreException("The provided key contains an illegal character (.)");
|
|
||||||
if (_dynamicChildren.ContainsKey(key))
|
|
||||||
{
|
|
||||||
throw new ArtemisCoreException($"Cannot add a dynamic child with key '{key}' " +
|
|
||||||
"because the key is already in use on by another dynamic property this data model.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GetType().GetProperty(key) != null)
|
|
||||||
{
|
|
||||||
throw new ArtemisCoreException($"Cannot add a dynamic child with key '{key}' " +
|
|
||||||
"because the key is already in use by a static property on this data model.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure a name is on the attribute or funny things might happen
|
|
||||||
attribute.Name ??= key.Humanize();
|
|
||||||
if (dynamicChild is DataModel dynamicDataModel)
|
|
||||||
{
|
|
||||||
dynamicDataModel.Feature = Feature;
|
|
||||||
dynamicDataModel.DataModelDescription = attribute;
|
|
||||||
}
|
|
||||||
|
|
||||||
_dynamicChildren.Add(key, new DynamicChild(attribute, dynamicChild));
|
|
||||||
|
|
||||||
OnDynamicDataModelAdded(new DynamicDataModelChildEventArgs(dynamicChild, key));
|
|
||||||
return dynamicChild;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes a dynamic child from the data model by its key
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">The key of the dynamic child to remove</param>
|
|
||||||
public void RemoveDynamicChildByKey(string key)
|
|
||||||
{
|
|
||||||
if (!_dynamicChildren.TryGetValue(key, out DynamicChild? dynamicChild))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_dynamicChildren.Remove(key);
|
|
||||||
OnDynamicDataModelRemoved(new DynamicDataModelChildEventArgs(dynamicChild.Value, key));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes a dynamic child from this data model
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dynamicChild">The dynamic data child to remove</param>
|
|
||||||
public void RemoveDynamicChild<T>(T dynamicChild) where T : class
|
|
||||||
{
|
|
||||||
List<string> keys = _dynamicChildren.Where(kvp => kvp.Value.Value == dynamicChild).Select(kvp => kvp.Key).ToList();
|
|
||||||
foreach (string key in keys)
|
|
||||||
{
|
|
||||||
_dynamicChildren.Remove(key);
|
|
||||||
OnDynamicDataModelRemoved(new DynamicDataModelChildEventArgs(dynamicChild, key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes all dynamic children from this data model
|
|
||||||
/// </summary>
|
|
||||||
public void ClearDynamicChildren()
|
|
||||||
{
|
|
||||||
while (_dynamicChildren.Any())
|
|
||||||
RemoveDynamicChildByKey(_dynamicChildren.First().Key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a dynamic child of type <typeparamref name="T" /> by its key
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of data model you expect</typeparam>
|
|
||||||
/// <param name="key">The unique key of the dynamic child</param>
|
|
||||||
/// <returns>If found, the dynamic child otherwise <c>null</c></returns>
|
|
||||||
public T? DynamicChild<T>(string key)
|
|
||||||
{
|
|
||||||
if (!_dynamicChildren.TryGetValue(key, out DynamicChild? dynamicChild))
|
|
||||||
return default;
|
|
||||||
|
|
||||||
if (dynamicChild.Value is not T)
|
|
||||||
return default;
|
|
||||||
return (T?) dynamicChild.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when a dynamic child has been added to this data model
|
/// Occurs when a dynamic child has been added to this data model
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -268,10 +92,184 @@ namespace Artemis.Core.DataModelExpansions
|
|||||||
{
|
{
|
||||||
DynamicChildRemoved?.Invoke(this, e);
|
DynamicChildRemoved?.Invoke(this, e);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
#region Dynamic children
|
||||||
/// Represents a record of a dynamic child value with its property attribute
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public record DynamicChild(DataModelPropertyAttribute Attribute, object? Value);
|
/// Adds a dynamic child to this data model
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key of the child, must be unique to this data model</param>
|
||||||
|
/// <param name="initialValue">The initial value of the dynamic child</param>
|
||||||
|
/// <returns>The resulting dynamic child which can be used to further update the value</returns>
|
||||||
|
public DynamicChild<T> AddDynamicChild<T>(string key, T initialValue)
|
||||||
|
{
|
||||||
|
return AddDynamicChild(key, initialValue, new DataModelPropertyAttribute());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a dynamic child to this data model
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key of the child, must be unique to this data model</param>
|
||||||
|
/// <param name="initialValue">The initial value of the dynamic child</param>
|
||||||
|
/// <param name="name">A human readable name for your dynamic child, shown in the UI</param>
|
||||||
|
/// <param name="description">An optional description, shown in the UI</param>
|
||||||
|
/// <returns>The resulting dynamic child which can be used to further update the value</returns>
|
||||||
|
public DynamicChild<T> AddDynamicChild<T>(string key, T initialValue, string name, string? description = null)
|
||||||
|
{
|
||||||
|
return AddDynamicChild(key, initialValue, new DataModelPropertyAttribute {Name = name, Description = description});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a dynamic child to this data model
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key of the child, must be unique to this data model</param>
|
||||||
|
/// <param name="initialValue">The initial value of the dynamic child</param>
|
||||||
|
/// <param name="attribute">A data model property attribute describing the dynamic child</param>
|
||||||
|
/// <returns>The resulting dynamic child which can be used to further update the value</returns>
|
||||||
|
public DynamicChild<T> AddDynamicChild<T>(string key, T initialValue, DataModelPropertyAttribute attribute)
|
||||||
|
{
|
||||||
|
if (key == null) throw new ArgumentNullException(nameof(key));
|
||||||
|
if (initialValue == null) throw new ArgumentNullException(nameof(initialValue));
|
||||||
|
if (attribute == null) throw new ArgumentNullException(nameof(attribute));
|
||||||
|
if (key.Contains('.'))
|
||||||
|
throw new ArtemisCoreException("The provided key contains an illegal character (.)");
|
||||||
|
if (_dynamicChildren.ContainsKey(key))
|
||||||
|
throw new ArtemisCoreException($"Cannot add a dynamic child with key '{key}' " +
|
||||||
|
"because the key is already in use on by another dynamic property this data model.");
|
||||||
|
|
||||||
|
if (GetType().GetProperty(key) != null)
|
||||||
|
throw new ArtemisCoreException($"Cannot add a dynamic child with key '{key}' " +
|
||||||
|
"because the key is already in use by a static property on this data model.");
|
||||||
|
|
||||||
|
// Make sure a name is on the attribute or funny things might happen
|
||||||
|
attribute.Name ??= key.Humanize();
|
||||||
|
if (initialValue is DataModel dynamicDataModel)
|
||||||
|
{
|
||||||
|
dynamicDataModel.Feature = Feature;
|
||||||
|
dynamicDataModel.DataModelDescription = attribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicChild<T> dynamicChild = new(initialValue, key, attribute);
|
||||||
|
_dynamicChildren.Add(key, dynamicChild);
|
||||||
|
|
||||||
|
OnDynamicDataModelAdded(new DynamicDataModelChildEventArgs(dynamicChild, key));
|
||||||
|
return dynamicChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a previously added dynamic child by its key
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key of the dynamic child</param>
|
||||||
|
public DynamicChild GetDynamicChild(string key)
|
||||||
|
{
|
||||||
|
if (key == null) throw new ArgumentNullException(nameof(key));
|
||||||
|
return DynamicChildren[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a previously added dynamic child by its key
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The typer of dynamic child you are expecting</typeparam>
|
||||||
|
/// <param name="key">The key of the dynamic child</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public DynamicChild<T> GetDynamicChild<T>(string key)
|
||||||
|
{
|
||||||
|
if (key == null) throw new ArgumentNullException(nameof(key));
|
||||||
|
return (DynamicChild<T>) DynamicChildren[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a previously added dynamic child by its key
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key of the dynamic child</param>
|
||||||
|
/// <param name="dynamicChild">
|
||||||
|
/// When this method returns, the <see cref="DynamicChild" /> associated with the specified key,
|
||||||
|
/// if the key is found; otherwise, <see langword="null" />. This parameter is passed uninitialized.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// <see langword="true" /> if the data model contains the dynamic child; otherwise <see langword="false" />
|
||||||
|
/// </returns>
|
||||||
|
public bool TryGetDynamicChild(string key, [MaybeNullWhen(false)] out DynamicChild dynamicChild)
|
||||||
|
{
|
||||||
|
if (key == null) throw new ArgumentNullException(nameof(key));
|
||||||
|
|
||||||
|
dynamicChild = null;
|
||||||
|
if (!DynamicChildren.TryGetValue(key, out DynamicChild? value))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
dynamicChild = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a previously added dynamic child by its key
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The typer of dynamic child you are expecting</typeparam>
|
||||||
|
/// <param name="key">The key of the dynamic child</param>
|
||||||
|
/// <param name="dynamicChild">
|
||||||
|
/// When this method returns, the <see cref="DynamicChild{T}" /> associated with the specified
|
||||||
|
/// key, if the key is found; otherwise, <see langword="null" />. This parameter is passed uninitialized.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// <see langword="true" /> if the data model contains the dynamic child; otherwise <see langword="false" />
|
||||||
|
/// </returns>
|
||||||
|
public bool TryGetDynamicChild<T>(string key, [MaybeNullWhen(false)] out DynamicChild<T> dynamicChild)
|
||||||
|
{
|
||||||
|
if (key == null) throw new ArgumentNullException(nameof(key));
|
||||||
|
|
||||||
|
dynamicChild = null;
|
||||||
|
if (!DynamicChildren.TryGetValue(key, out DynamicChild? value))
|
||||||
|
return false;
|
||||||
|
if (value is not DynamicChild<T> typedDynamicChild)
|
||||||
|
return false;
|
||||||
|
dynamicChild = typedDynamicChild;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a dynamic child from the data model by its key
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key of the dynamic child to remove</param>
|
||||||
|
public void RemoveDynamicChildByKey(string key)
|
||||||
|
{
|
||||||
|
if (key == null) throw new ArgumentNullException(nameof(key));
|
||||||
|
if (!_dynamicChildren.TryGetValue(key, out DynamicChild? dynamicChild))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_dynamicChildren.Remove(key);
|
||||||
|
OnDynamicDataModelRemoved(new DynamicDataModelChildEventArgs(dynamicChild, key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a dynamic child from this data model
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dynamicChild">The dynamic data child to remove</param>
|
||||||
|
public void RemoveDynamicChild(DynamicChild dynamicChild)
|
||||||
|
{
|
||||||
|
if (dynamicChild == null) throw new ArgumentNullException(nameof(dynamicChild));
|
||||||
|
List<string> keys = _dynamicChildren.Where(kvp => kvp.Value.BaseValue == dynamicChild).Select(kvp => kvp.Key).ToList();
|
||||||
|
foreach (string key in keys)
|
||||||
|
{
|
||||||
|
_dynamicChildren.Remove(key);
|
||||||
|
OnDynamicDataModelRemoved(new DynamicDataModelChildEventArgs(dynamicChild, key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes all dynamic children from this data model
|
||||||
|
/// </summary>
|
||||||
|
public void ClearDynamicChildren()
|
||||||
|
{
|
||||||
|
while (_dynamicChildren.Any())
|
||||||
|
RemoveDynamicChildByKey(_dynamicChildren.First().Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used a runtime by data model paths only
|
||||||
|
internal T? GetDynamicChildValue<T>(string key)
|
||||||
|
{
|
||||||
|
return TryGetDynamicChild(key, out DynamicChild<T>? dynamicChild) ? dynamicChild.Value : default;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
}
|
}
|
||||||
66
src/Artemis.Core/Plugins/DataModelExpansions/DynamicChild.cs
Normal file
66
src/Artemis.Core/Plugins/DataModelExpansions/DynamicChild.cs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Artemis.Core.DataModelExpansions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a dynamic child value with its property attribute
|
||||||
|
/// </summary>
|
||||||
|
public class DynamicChild<T> : DynamicChild
|
||||||
|
{
|
||||||
|
internal DynamicChild(T value, string key, DataModelPropertyAttribute attribute) : base(key, attribute, typeof(T))
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the current value of the dynamic child
|
||||||
|
/// </summary>
|
||||||
|
public new T Value { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override object? GetValue()
|
||||||
|
{
|
||||||
|
return Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a dynamic child value with its property attribute
|
||||||
|
/// </summary>
|
||||||
|
public abstract class DynamicChild
|
||||||
|
{
|
||||||
|
internal DynamicChild(string key, DataModelPropertyAttribute attribute, Type type)
|
||||||
|
{
|
||||||
|
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||||
|
Key = key ?? throw new ArgumentNullException(nameof(key));
|
||||||
|
Attribute = attribute ?? throw new ArgumentNullException(nameof(attribute));
|
||||||
|
Type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the key of the dynamic child
|
||||||
|
/// </summary>
|
||||||
|
public string Key { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the attribute describing the dynamic child
|
||||||
|
/// </summary>
|
||||||
|
public DataModelPropertyAttribute Attribute { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the type of <see cref="BaseValue" />
|
||||||
|
/// </summary>
|
||||||
|
public Type Type { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current value of the dynamic child
|
||||||
|
/// </summary>
|
||||||
|
public object? BaseValue => GetValue();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current value of the dynamic child
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The current value of the dynamic child</returns>
|
||||||
|
protected abstract object? GetValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -113,7 +113,7 @@ namespace Artemis.Core.Services
|
|||||||
|
|
||||||
private void SurfaceOnException(ExceptionEventArgs args)
|
private void SurfaceOnException(ExceptionEventArgs args)
|
||||||
{
|
{
|
||||||
_logger.Warning("Surface threw e");
|
_logger.Warning(args.Exception, "Surface caught exception");
|
||||||
throw args.Exception;
|
throw args.Exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -314,6 +314,7 @@ namespace Artemis.UI.Shared
|
|||||||
ImageBrush bitmapBrush = new(bitmap);
|
ImageBrush bitmapBrush = new(bitmap);
|
||||||
bitmapBrush.Freeze();
|
bitmapBrush.Freeze();
|
||||||
_backingStore.OpacityMask = bitmapBrush;
|
_backingStore.OpacityMask = bitmapBrush;
|
||||||
|
_backingStore.Children.Clear();
|
||||||
|
|
||||||
InvalidateMeasure();
|
InvalidateMeasure();
|
||||||
}
|
}
|
||||||
@ -331,14 +332,19 @@ namespace Artemis.UI.Shared
|
|||||||
private void Render()
|
private void Render()
|
||||||
{
|
{
|
||||||
DrawingContext drawingContext = _backingStore.Append();
|
DrawingContext drawingContext = _backingStore.Append();
|
||||||
// DrawingContext drawingContext = _backingStore.Open();
|
|
||||||
|
|
||||||
if (HighlightedLeds != null && HighlightedLeds.Any())
|
if (HighlightedLeds != null && HighlightedLeds.Any())
|
||||||
|
{
|
||||||
|
// Faster on large devices, maybe a bit slower on smaller ones but that's ok
|
||||||
|
ILookup<ArtemisLed, ArtemisLed> toHighlight = HighlightedLeds.ToLookup(l => l);
|
||||||
foreach (DeviceVisualizerLed deviceVisualizerLed in _deviceVisualizerLeds)
|
foreach (DeviceVisualizerLed deviceVisualizerLed in _deviceVisualizerLeds)
|
||||||
deviceVisualizerLed.RenderColor(_backingStore, drawingContext, !HighlightedLeds.Contains(deviceVisualizerLed.Led));
|
deviceVisualizerLed.RenderColor(_backingStore, drawingContext, !toHighlight.Contains(deviceVisualizerLed.Led));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
foreach (DeviceVisualizerLed deviceVisualizerLed in _deviceVisualizerLeds)
|
foreach (DeviceVisualizerLed deviceVisualizerLed in _deviceVisualizerLeds)
|
||||||
deviceVisualizerLed.RenderColor(_backingStore, drawingContext, false);
|
deviceVisualizerLed.RenderColor(_backingStore, drawingContext, false);
|
||||||
|
}
|
||||||
|
|
||||||
drawingContext.Close();
|
drawingContext.Close();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,10 +14,10 @@ namespace Artemis.UI.Shared
|
|||||||
{
|
{
|
||||||
private const byte Dimmed = 100;
|
private const byte Dimmed = 100;
|
||||||
private const byte NonDimmed = 255;
|
private const byte NonDimmed = 255;
|
||||||
private GeometryDrawing? _geometryDrawing;
|
|
||||||
private Color _renderColor;
|
private Color _renderColor;
|
||||||
|
private GeometryDrawing? _geometryDrawing;
|
||||||
private SolidColorBrush? _renderColorBrush;
|
private SolidColorBrush? _renderColorBrush;
|
||||||
|
private DrawingGroup? _lastBackingStore;
|
||||||
|
|
||||||
public DeviceVisualizerLed(ArtemisLed led)
|
public DeviceVisualizerLed(ArtemisLed led)
|
||||||
{
|
{
|
||||||
@ -43,12 +43,12 @@ namespace Artemis.UI.Shared
|
|||||||
|
|
||||||
public void RenderColor(DrawingGroup backingStore, DrawingContext drawingContext, bool isDimmed)
|
public void RenderColor(DrawingGroup backingStore, DrawingContext drawingContext, bool isDimmed)
|
||||||
{
|
{
|
||||||
if (DisplayGeometry == null)
|
if (DisplayGeometry == null || backingStore == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_renderColorBrush ??= new SolidColorBrush();
|
_renderColorBrush ??= new SolidColorBrush();
|
||||||
_geometryDrawing ??= new GeometryDrawing(_renderColorBrush, null, new RectangleGeometry(LedRect));
|
_geometryDrawing ??= new GeometryDrawing(_renderColorBrush, null, new RectangleGeometry(LedRect));
|
||||||
|
|
||||||
byte r = Led.RgbLed.Color.GetR();
|
byte r = Led.RgbLed.Color.GetR();
|
||||||
byte g = Led.RgbLed.Color.GetG();
|
byte g = Led.RgbLed.Color.GetG();
|
||||||
byte b = Led.RgbLed.Color.GetB();
|
byte b = Led.RgbLed.Color.GetB();
|
||||||
@ -59,9 +59,12 @@ namespace Artemis.UI.Shared
|
|||||||
_renderColor.B = b;
|
_renderColor.B = b;
|
||||||
_renderColorBrush.Color = _renderColor;
|
_renderColorBrush.Color = _renderColor;
|
||||||
|
|
||||||
|
if (_lastBackingStore != backingStore)
|
||||||
if (!backingStore.Children.Contains(_geometryDrawing))
|
{
|
||||||
backingStore.Children.Add(_geometryDrawing);
|
backingStore.Children.Add(_geometryDrawing);
|
||||||
|
_lastBackingStore = backingStore;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RenderImage(DrawingContext drawingContext)
|
public void RenderImage(DrawingContext drawingContext)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user