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

Core - Fixed data model namespaces

Modules - Added IsPropertyInUse API
UI - Properly dispose data model paths wherever they are used
This commit is contained in:
Robert 2021-06-11 23:20:14 +02:00
parent 222fddd749
commit 3f22ebae8a
48 changed files with 305 additions and 103 deletions

View File

@ -57,6 +57,7 @@
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Clayerbrushes_005Cinternal/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Clayerbrushes_005Cinternal/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Clayereffects_005Cinternal/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Clayereffects_005Cinternal/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Cmodules_005Cactivationrequirements/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Cmodules_005Cactivationrequirements/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Cmodules_005Cattributes/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Cprerequisites/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Cprerequisites/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Cprerequisites_005Cprerequisiteaction/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Cprerequisites_005Cprerequisiteaction/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Cprofiling/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Cprofiling/@EntryIndexedValue">True</s:Boolean>

View File

@ -0,0 +1,20 @@
using System;
namespace Artemis.Core
{
/// <summary>
/// Provides data about data model path related events
/// </summary>
public class DataModelPathEventArgs : EventArgs
{
internal DataModelPathEventArgs(DataModelPath dataModelPath)
{
DataModelPath = dataModelPath;
}
/// <summary>
/// Gets the data model path this event is related to
/// </summary>
public DataModelPath DataModelPath { get; }
}
}

View File

@ -1,5 +1,5 @@
using System; using System;
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
namespace Artemis.Core namespace Artemis.Core
{ {

View File

@ -1,5 +1,5 @@
using System; using System;
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
namespace Artemis.Core namespace Artemis.Core
{ {

View File

@ -1,5 +1,5 @@
using System; using System;
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
namespace Artemis.Core namespace Artemis.Core
{ {

View File

@ -1,5 +1,5 @@
using System; using System;
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
namespace Artemis.Core namespace Artemis.Core
{ {

View File

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
namespace Artemis.Core namespace Artemis.Core
{ {

View File

@ -1,5 +1,5 @@
using System; using System;
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
namespace Artemis.Core namespace Artemis.Core
{ {

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
using Artemis.Storage.Entities.Profile; using Artemis.Storage.Entities.Profile;
namespace Artemis.Core namespace Artemis.Core
@ -175,6 +175,8 @@ namespace Artemis.Core
internal void Invalidate() internal void Invalidate()
{ {
Target?.RemoveDataModelPath(this);
foreach (DataModelPathSegment dataModelPathSegment in _segments) foreach (DataModelPathSegment dataModelPathSegment in _segments)
dataModelPathSegment.Dispose(); dataModelPathSegment.Dispose();
_segments.Clear(); _segments.Clear();
@ -187,11 +189,11 @@ namespace Artemis.Core
internal void Initialize() internal void Initialize()
{ {
Invalidate();
if (Target == null) if (Target == null)
return; return;
Target.AddDataModelPath(this);
DataModelPathSegment startSegment = new(this, "target", "target"); DataModelPathSegment startSegment = new(this, "target", "target");
startSegment.Node = _segments.AddFirst(startSegment); startSegment.Node = _segments.AddFirst(startSegment);
@ -330,6 +332,7 @@ namespace Artemis.Core
if (e.Registration.DataModel.Module.Id != Entity.DataModelId) if (e.Registration.DataModel.Module.Id != Entity.DataModelId)
return; return;
Invalidate();
Target = e.Registration.DataModel; Target = e.Registration.DataModel;
Initialize(); Initialize();
} }
@ -339,8 +342,8 @@ namespace Artemis.Core
if (e.Registration.DataModel.Module.Id != Entity.DataModelId) if (e.Registration.DataModel.Module.Id != Entity.DataModelId)
return; return;
Target = null;
Invalidate(); Invalidate();
Target = null;
} }
#endregion #endregion

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
using Humanizer; using Humanizer;
namespace Artemis.Core namespace Artemis.Core
@ -301,7 +301,7 @@ namespace Artemis.Core
private void DynamicChildOnDynamicChildRemoved(object? sender, DynamicDataModelChildEventArgs e) private void DynamicChildOnDynamicChildRemoved(object? sender, DynamicDataModelChildEventArgs e)
{ {
if (e.DynamicChild.BaseValue == _dynamicDataModel) if (e.DynamicChild.BaseValue == _dynamicDataModel)
DataModelPath.Initialize(); DataModelPath.Invalidate();
} }
#endregion #endregion

View File

@ -1,4 +1,4 @@
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
namespace Artemis.Core namespace Artemis.Core
{ {

View File

@ -5,6 +5,9 @@ using Artemis.Storage.Entities.Profile;
namespace Artemis.Core namespace Artemis.Core
{ {
/// <summary>
/// Represents a category containing <see cref="ProfileConfigurations" />
/// </summary>
public class ProfileCategory : CorePropertyChanged, IStorageModel public class ProfileCategory : CorePropertyChanged, IStorageModel
{ {
private readonly List<ProfileConfiguration> _profileConfigurations = new(); private readonly List<ProfileConfiguration> _profileConfigurations = new();

View File

@ -59,6 +59,8 @@ namespace Artemis.Core
foreach (BaseLayerEffect baseLayerEffect in LayerEffects) foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
baseLayerEffect.Dispose(); baseLayerEffect.Dispose();
DisplayCondition?.Dispose();
base.Dispose(disposing); base.Dispose(disposing);
} }

View File

@ -1,13 +1,18 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Artemis.Core.Modules; using Artemis.Core.Modules;
using Artemis.Storage.Entities.Profile; using Artemis.Storage.Entities.Profile;
namespace Artemis.Core namespace Artemis.Core
{ {
public class ProfileConfiguration : CorePropertyChanged, IStorageModel /// <summary>
/// Represents the configuration of a profile, contained in a <see cref="ProfileCategory" />
/// </summary>
public class ProfileConfiguration : CorePropertyChanged, IStorageModel, IDisposable
{ {
private ProfileCategory _category; private ProfileCategory _category;
private bool _disposed;
private bool _isMissingModule; private bool _isMissingModule;
private bool _isSuspended; private bool _isSuspended;
@ -140,11 +145,17 @@ namespace Artemis.Core
/// </summary> /// </summary>
public void Update() public void Update()
{ {
if (_disposed)
throw new ObjectDisposedException("ProfileConfiguration");
ActivationConditionMet = ActivationCondition == null || ActivationCondition.Evaluate(); ActivationConditionMet = ActivationCondition == null || ActivationCondition.Evaluate();
} }
public bool ShouldBeActive(bool includeActivationCondition) public bool ShouldBeActive(bool includeActivationCondition)
{ {
if (_disposed)
throw new ObjectDisposedException("ProfileConfiguration");
if (Category.IsSuspended || IsSuspended || IsMissingModule) if (Category.IsSuspended || IsSuspended || IsMissingModule)
return false; return false;
@ -161,15 +172,32 @@ namespace Artemis.Core
internal void LoadModules(List<Module> enabledModules) internal void LoadModules(List<Module> enabledModules)
{ {
if (_disposed)
throw new ObjectDisposedException("ProfileConfiguration");
Module = enabledModules.FirstOrDefault(m => m.Id == Entity.ModuleId); Module = enabledModules.FirstOrDefault(m => m.Id == Entity.ModuleId);
IsMissingModule = Module == null && Entity.ModuleId != null; IsMissingModule = Module == null && Entity.ModuleId != null;
} }
#region IDisposable
/// <inheritdoc />
public void Dispose()
{
_disposed = true;
ActivationCondition?.Dispose();
}
#endregion
#region Implementation of IStorageModel #region Implementation of IStorageModel
/// <inheritdoc /> /// <inheritdoc />
public void Load() public void Load()
{ {
if (_disposed)
throw new ObjectDisposedException("ProfileConfiguration");
Name = Entity.Name; Name = Entity.Name;
IsSuspended = Entity.IsSuspended; IsSuspended = Entity.IsSuspended;
ActivationBehaviour = (ActivationBehaviour) Entity.ActivationBehaviour; ActivationBehaviour = (ActivationBehaviour) Entity.ActivationBehaviour;
@ -185,6 +213,9 @@ namespace Artemis.Core
/// <inheritdoc /> /// <inheritdoc />
public void Save() public void Save()
{ {
if (_disposed)
throw new ObjectDisposedException("ProfileConfiguration");
Entity.Name = Name; Entity.Name = Name;
Entity.IsSuspended = IsSuspended; Entity.IsSuspended = IsSuspended;
Entity.ActivationBehaviour = (int) ActivationBehaviour; Entity.ActivationBehaviour = (int) ActivationBehaviour;
@ -199,9 +230,7 @@ namespace Artemis.Core
Entity.ActivationCondition = ActivationCondition.Entity; Entity.ActivationCondition = ActivationCondition.Entity;
} }
else else
{
Entity.ActivationCondition = null; Entity.ActivationCondition = null;
}
if (!IsMissingModule) if (!IsMissingModule)
Entity.ModuleId = Module?.Id; Entity.ModuleId = Module?.Id;

View File

@ -1,6 +1,6 @@
using System; using System;
namespace Artemis.Core.DataModelExpansions namespace Artemis.Core.Modules
{ {
/// <summary> /// <summary>
/// Represents an attribute that marks a data model property to be ignored by the UI /// Represents an attribute that marks a data model property to be ignored by the UI

View File

@ -1,6 +1,6 @@
using System; using System;
namespace Artemis.Core.DataModelExpansions namespace Artemis.Core.Modules
{ {
/// <summary> /// <summary>
/// Represents an attribute that describes a data model property /// Represents an attribute that describes a data model property

View File

@ -6,15 +6,15 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using Humanizer; using Humanizer;
using Newtonsoft.Json; using Newtonsoft.Json;
using Module = Artemis.Core.Modules.Module;
namespace Artemis.Core.DataModelExpansions namespace Artemis.Core.Modules
{ {
/// <summary> /// <summary>
/// Represents a data model that contains information on a game/application etc. /// Represents a data model that contains information on a game/application etc.
/// </summary> /// </summary>
public abstract class DataModel public abstract class DataModel
{ {
private readonly HashSet<string> _activePathsHashSet = new();
private readonly List<DataModelPath> _activePaths = new(); private readonly List<DataModelPath> _activePaths = new();
private readonly Dictionary<string, DynamicChild> _dynamicChildren = new(); private readonly Dictionary<string, DynamicChild> _dynamicChildren = new();
@ -65,36 +65,7 @@ namespace Artemis.Core.DataModelExpansions
/// <returns></returns> /// <returns></returns>
public ReadOnlyCollection<PropertyInfo> GetHiddenProperties() public ReadOnlyCollection<PropertyInfo> GetHiddenProperties()
{ {
if (Module is Module module) return Module.HiddenProperties;
return module.HiddenProperties;
return new List<PropertyInfo>().AsReadOnly();
}
/// <summary>
/// Occurs when a dynamic child has been added to this data model
/// </summary>
public event EventHandler<DynamicDataModelChildEventArgs>? DynamicChildAdded;
/// <summary>
/// Occurs when a dynamic child has been removed from this data model
/// </summary>
public event EventHandler<DynamicDataModelChildEventArgs>? DynamicChildRemoved;
/// <summary>
/// Invokes the <see cref="DynamicChildAdded" /> event
/// </summary>
protected virtual void OnDynamicDataModelAdded(DynamicDataModelChildEventArgs e)
{
DynamicChildAdded?.Invoke(this, e);
}
/// <summary>
/// Invokes the <see cref="DynamicChildRemoved" /> event
/// </summary>
protected virtual void OnDynamicDataModelRemoved(DynamicDataModelChildEventArgs e)
{
DynamicChildRemoved?.Invoke(this, e);
} }
#region Dynamic children #region Dynamic children
@ -285,15 +256,103 @@ namespace Artemis.Core.DataModelExpansions
#region Paths #region Paths
/// <summary>
/// Determines whether the provided dot-separated path is in use
/// </summary>
/// <param name="path">The path to check per example: <c>MyDataModelChild.MyDataModelProperty</c></param>
/// <param name="includeChildren">
/// If <see langword="true" /> any child of the given path will return true as well; if
/// <see langword="false" /> only an exact path match returns <see langword="true" />.
/// </param>
internal bool IsPropertyInUse(string path, bool includeChildren)
{
path = path.ToUpperInvariant();
return includeChildren
? _activePathsHashSet.Any(p => p.StartsWith(path, StringComparison.Ordinal))
: _activePathsHashSet.Contains(path);
}
internal void AddDataModelPath(DataModelPath path) internal void AddDataModelPath(DataModelPath path)
{ {
if (!_activePaths.Contains(path)) if (_activePaths.Contains(path))
_activePaths.Add(path); return;
_activePaths.Add(path);
// Add to the hashset if this is the first path pointing
string hashPath = path.Path.ToUpperInvariant();
if (!_activePathsHashSet.Contains(hashPath))
_activePathsHashSet.Add(hashPath);
OnActivePathAdded(new DataModelPathEventArgs(path));
} }
internal void RemoveDataModelPath(DataModelPath path) internal void RemoveDataModelPath(DataModelPath path)
{ {
_activePaths.Remove(path); if (!_activePaths.Remove(path))
return;
// Remove from the hashset if this was the last path pointing there
if (_activePaths.All(p => p.Path != path.Path))
_activePathsHashSet.Remove(path.Path.ToUpperInvariant());
OnActivePathRemoved(new DataModelPathEventArgs(path));
}
#endregion
#region Events
/// <summary>
/// Occurs when a dynamic child has been added to this data model
/// </summary>
public event EventHandler<DynamicDataModelChildEventArgs>? DynamicChildAdded;
/// <summary>
/// Occurs when a dynamic child has been removed from this data model
/// </summary>
public event EventHandler<DynamicDataModelChildEventArgs>? DynamicChildRemoved;
/// <summary>
/// Occurs when a dynamic child has been added to this data model
/// </summary>
public event EventHandler<DataModelPathEventArgs>? ActivePathAdded;
/// <summary>
/// Occurs when a dynamic child has been removed from this data model
/// </summary>
public event EventHandler<DataModelPathEventArgs>? ActivePathRemoved;
/// <summary>
/// Invokes the <see cref="DynamicChildAdded" /> event
/// </summary>
protected virtual void OnDynamicDataModelAdded(DynamicDataModelChildEventArgs e)
{
DynamicChildAdded?.Invoke(this, e);
}
/// <summary>
/// Invokes the <see cref="DynamicChildRemoved" /> event
/// </summary>
protected virtual void OnDynamicDataModelRemoved(DynamicDataModelChildEventArgs e)
{
DynamicChildRemoved?.Invoke(this, e);
}
/// <summary>
/// Invokes the <see cref="ActivePathAdded" /> event
/// </summary>
protected virtual void OnActivePathAdded(DataModelPathEventArgs e)
{
ActivePathAdded?.Invoke(this, e);
}
/// <summary>
/// Invokes the <see cref="ActivePathRemoved" /> event
/// </summary>
protected virtual void OnActivePathRemoved(DataModelPathEventArgs e)
{
ActivePathRemoved?.Invoke(this, e);
} }
#endregion #endregion

View File

@ -1,6 +1,6 @@
using System; using System;
namespace Artemis.Core.DataModelExpansions namespace Artemis.Core.Modules
{ {
/// <summary> /// <summary>
/// Represents a dynamic child value with its property attribute /// Represents a dynamic child value with its property attribute

View File

@ -5,7 +5,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using Artemis.Core.DataModelExpansions; using System.Text;
namespace Artemis.Core.Modules namespace Artemis.Core.Modules
{ {
@ -25,7 +25,8 @@ namespace Artemis.Core.Modules
} }
/// <summary> /// <summary>
/// Hide the provided property using a lambda expression, e.g. HideProperty(dm => dm.TimeDataModel.CurrentTimeUTC) /// Hide the provided property using a lambda expression, e.g.
/// <c>HideProperty(dm => dm.TimeDataModel.CurrentTimeUTC)</c>
/// </summary> /// </summary>
/// <param name="propertyLambda">A lambda expression pointing to the property to ignore</param> /// <param name="propertyLambda">A lambda expression pointing to the property to ignore</param>
public void HideProperty<TProperty>(Expression<Func<T, TProperty>> propertyLambda) public void HideProperty<TProperty>(Expression<Func<T, TProperty>> propertyLambda)
@ -36,8 +37,8 @@ namespace Artemis.Core.Modules
} }
/// <summary> /// <summary>
/// Stop hiding the provided property using a lambda expression, e.g. ShowProperty(dm => /// Stop hiding the provided property using a lambda expression, e.g.
/// dm.TimeDataModel.CurrentTimeUTC) /// <c>ShowProperty(dm => dm.TimeDataModel.CurrentTimeUTC)</c>
/// </summary> /// </summary>
/// <param name="propertyLambda">A lambda expression pointing to the property to stop ignoring</param> /// <param name="propertyLambda">A lambda expression pointing to the property to stop ignoring</param>
public void ShowProperty<TProperty>(Expression<Func<T, TProperty>> propertyLambda) public void ShowProperty<TProperty>(Expression<Func<T, TProperty>> propertyLambda)
@ -46,6 +47,36 @@ namespace Artemis.Core.Modules
HiddenPropertiesList.RemoveAll(p => p.Equals(propertyInfo)); HiddenPropertiesList.RemoveAll(p => p.Equals(propertyInfo));
} }
/// <summary>
/// Determines whether the provided dot-separated path is actively being used by Artemis
/// <para>Note: <see cref="IsPropertyInUse" /> is slightly faster but string-based.</para>
/// </summary>
/// <param name="propertyLambda">
/// The path to check per example: <c>IsPropertyInUse(dm => dm.TimeDataModel.CurrentTimeUTC)</c>
/// </param>
/// <param name="includeChildren">
/// If <see langword="true" /> any child of the given path will return true as well; if
/// <see langword="false" /> only an exact path match returns <see langword="true" />.
/// </param>
public bool IsPropertyInUse<TProperty>(Expression<Func<T, TProperty>> propertyLambda, bool includeChildren)
{
string path = GetMemberPath((MemberExpression) propertyLambda.Body);
return IsPropertyInUse(path, includeChildren);
}
/// <summary>
/// Determines whether the provided dot-separated path is actively being used by Artemis
/// </summary>
/// <param name="path">The path to check per example: <c>MyDataModelChild.MyDataModelProperty</c></param>
/// <param name="includeChildren">
/// If <see langword="true" /> any child of the given path will return true as well; if
/// <see langword="false" /> only an exact path match returns <see langword="true" />.
/// </param>
public bool IsPropertyInUse(string path, bool includeChildren)
{
return DataModel.IsPropertyInUse(path, includeChildren);
}
internal override void InternalEnable() internal override void InternalEnable()
{ {
DataModel = Activator.CreateInstance<T>(); DataModel = Activator.CreateInstance<T>();
@ -59,6 +90,20 @@ namespace Artemis.Core.Modules
Deactivate(true); Deactivate(true);
base.InternalDisable(); base.InternalDisable();
} }
private static string GetMemberPath(MemberExpression? me)
{
StringBuilder builder = new();
while (me != null)
{
builder.Insert(0, me.Member.Name);
me = me.Expression as MemberExpression;
if (me != null)
builder.Insert(0, ".");
}
return builder.ToString();
}
} }
/// <summary> /// <summary>

View File

@ -4,7 +4,6 @@ using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core.DataModelExpansions;
using Artemis.Core.Ninject; using Artemis.Core.Ninject;
using Artemis.Storage; using Artemis.Storage;
using HidSharp; using HidSharp;

View File

@ -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.DataModelExpansions;
using Artemis.Core.Modules; using Artemis.Core.Modules;
namespace Artemis.Core.Services namespace Artemis.Core.Services

View File

@ -1,6 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
namespace Artemis.Core.Services namespace Artemis.Core.Services
{ {

View File

@ -357,6 +357,8 @@ namespace Artemis.Core.Services
ProfileEntity profileEntity = _profileRepository.Get(profileConfiguration.Entity.ProfileId); ProfileEntity profileEntity = _profileRepository.Get(profileConfiguration.Entity.ProfileId);
if (profileEntity != null) if (profileEntity != null)
_profileRepository.Remove(profileEntity); _profileRepository.Remove(profileEntity);
profileConfiguration.Dispose();
} }
public void SaveProfileCategory(ProfileCategory profileCategory) public void SaveProfileCategory(ProfileCategory profileCategory)

View File

@ -1,7 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core.DataModelExpansions;
using Artemis.Core.Modules; using Artemis.Core.Modules;
using EmbedIO; using EmbedIO;
using Newtonsoft.Json; using Newtonsoft.Json;

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core.DataModelExpansions;
using Artemis.Core.Modules; using Artemis.Core.Modules;
using EmbedIO; using EmbedIO;
using EmbedIO.WebApi; using EmbedIO.WebApi;

View File

@ -4,7 +4,6 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core.DataModelExpansions;
using Artemis.Core.Modules; using Artemis.Core.Modules;
using EmbedIO; using EmbedIO;
using EmbedIO.WebApi; using EmbedIO.WebApi;

View File

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
namespace Artemis.Core namespace Artemis.Core
{ {

View File

@ -1,5 +1,5 @@
using System; using System;
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
namespace Artemis.Core namespace Artemis.Core
{ {

View File

@ -1,5 +1,5 @@
using System.Linq.Expressions; using System.Linq.Expressions;
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
namespace Artemis.Core namespace Artemis.Core
{ {

View File

@ -1,5 +1,5 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
using Stylet; using Stylet;
namespace Artemis.UI.Shared namespace Artemis.UI.Shared

View File

@ -5,7 +5,7 @@ using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Data; using System.Windows.Data;
using System.Windows.Input; using System.Windows.Input;
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
using Stylet; using Stylet;
namespace Artemis.UI.Shared namespace Artemis.UI.Shared

View File

@ -272,6 +272,7 @@ namespace Artemis.UI.Shared.Input
_updateTimer.Dispose(); _updateTimer.Dispose();
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed; _updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
DataModelViewModel?.Dispose();
DataModelPath?.Dispose(); DataModelPath?.Dispose();
} }
} }

View File

@ -4,7 +4,7 @@ using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using MaterialDesignColors.ColorManipulation; using MaterialDesignColors.ColorManipulation;
using Stylet; using Stylet;

View File

@ -1,7 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
namespace Artemis.UI.Shared namespace Artemis.UI.Shared

View File

@ -2,7 +2,7 @@
using System.Collections; using System.Collections;
using System.Windows.Documents; using System.Windows.Documents;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Stylet; using Stylet;

View File

@ -1,6 +1,6 @@
using System; using System;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
namespace Artemis.UI.Shared namespace Artemis.UI.Shared

View File

@ -1,6 +1,6 @@
using System; using System;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
namespace Artemis.UI.Shared namespace Artemis.UI.Shared

View File

@ -3,8 +3,9 @@ using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Stylet; using Stylet;
@ -13,7 +14,7 @@ namespace Artemis.UI.Shared
/// <summary> /// <summary>
/// Represents a base class for a view model that visualizes a part of the data model /// Represents a base class for a view model that visualizes a part of the data model
/// </summary> /// </summary>
public abstract class DataModelVisualizationViewModel : PropertyChangedBase public abstract class DataModelVisualizationViewModel : PropertyChangedBase, IDisposable
{ {
private const int MaxDepth = 4; private const int MaxDepth = 4;
private BindableCollection<DataModelVisualizationViewModel> _children; private BindableCollection<DataModelVisualizationViewModel> _children;
@ -22,6 +23,7 @@ namespace Artemis.UI.Shared
private bool _isVisualizationExpanded; private bool _isVisualizationExpanded;
private DataModelVisualizationViewModel? _parent; private DataModelVisualizationViewModel? _parent;
private DataModelPropertyAttribute? _propertyDescription; private DataModelPropertyAttribute? _propertyDescription;
private bool _populatedStaticChildren;
internal DataModelVisualizationViewModel(DataModel? dataModel, DataModelVisualizationViewModel? parent, DataModelPath? dataModelPath) internal DataModelVisualizationViewModel(DataModel? dataModel, DataModelVisualizationViewModel? parent, DataModelPath? dataModelPath)
{ {
@ -206,21 +208,26 @@ namespace Artemis.UI.Shared
if (modelType == null) if (modelType == null)
throw new ArtemisSharedUIException("Failed to populate data model visualization properties, couldn't get a property type"); throw new ArtemisSharedUIException("Failed to populate data model visualization properties, couldn't get a property type");
// Add missing static children // Add missing static children only once, they're static after all
foreach (PropertyInfo propertyInfo in modelType.GetProperties(BindingFlags.Public | BindingFlags.Instance).OrderBy(t => t.MetadataToken)) if (!_populatedStaticChildren)
{ {
string childPath = AppendToPath(propertyInfo.Name); foreach (PropertyInfo propertyInfo in modelType.GetProperties(BindingFlags.Public | BindingFlags.Instance).OrderBy(t => t.MetadataToken))
if (Children.Any(c => c.Path != null && c.Path.Equals(childPath))) {
continue; string childPath = AppendToPath(propertyInfo.Name);
if (propertyInfo.GetCustomAttribute<DataModelIgnoreAttribute>() != null) if (Children.Any(c => c.Path != null && c.Path.Equals(childPath)))
continue; continue;
MethodInfo? getMethod = propertyInfo.GetGetMethod(); if (propertyInfo.GetCustomAttribute<DataModelIgnoreAttribute>() != null)
if (getMethod == null || getMethod.GetParameters().Any()) continue;
continue; MethodInfo? getMethod = propertyInfo.GetGetMethod();
if (getMethod == null || getMethod.GetParameters().Any())
continue;
DataModelVisualizationViewModel? child = CreateChild(dataModelUIService, childPath, GetChildDepth()); DataModelVisualizationViewModel? child = CreateChild(dataModelUIService, childPath, GetChildDepth());
if (child != null) if (child != null)
Children.Add(child); Children.Add(child);
}
_populatedStaticChildren = true;
} }
// Remove static children that should be hidden // Remove static children that should be hidden
@ -302,7 +309,14 @@ namespace Artemis.UI.Shared
private string AppendToPath(string toAppend) private string AppendToPath(string toAppend)
{ {
return !string.IsNullOrEmpty(Path) ? $"{Path}.{toAppend}" : toAppend; if (string.IsNullOrEmpty(Path))
return toAppend;
StringBuilder builder = new();
builder.Append(Path);
builder.Append(".");
builder.Append(toAppend);
return builder.ToString();
} }
private void RequestUpdate() private void RequestUpdate()
@ -327,5 +341,33 @@ namespace Artemis.UI.Shared
} }
#endregion #endregion
#region IDisposable
/// <summary>
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
DataModelPath?.Dispose();
foreach (DataModelVisualizationViewModel dataModelVisualizationViewModel in Children)
dataModelVisualizationViewModel.Dispose(true);
}
}
/// <inheritdoc />
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
} }
} }

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Artemis.Core.DataModelExpansions;
using Artemis.Core.Modules; using Artemis.Core.Modules;
using Artemis.UI.Shared.Input; using Artemis.UI.Shared.Input;

View File

@ -2,7 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.DataModelExpansions;
using Artemis.Core.Modules; using Artemis.Core.Modules;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Shared.DefaultTypes.DataModel.Display; using Artemis.UI.Shared.DefaultTypes.DataModel.Display;

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.DataModelExpansions;
using Artemis.Core.Modules; using Artemis.Core.Modules;
using Artemis.UI.Shared.Input; using Artemis.UI.Shared.Input;

View File

@ -1,4 +1,4 @@
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
using Artemis.UI.Shared; using Artemis.UI.Shared;
namespace Artemis.UI.DefaultTypes.DataModel.Input namespace Artemis.UI.DefaultTypes.DataModel.Input

View File

@ -1,7 +1,7 @@
using System.Globalization; using System.Globalization;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Windows.Input; using System.Windows.Input;
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
using Artemis.UI.Shared; using Artemis.UI.Shared;
namespace Artemis.UI.DefaultTypes.DataModel.Input namespace Artemis.UI.DefaultTypes.DataModel.Input

View File

@ -1,5 +1,5 @@
using System; using System;
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Stylet; using Stylet;

View File

@ -1,6 +1,6 @@
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Windows.Input; using System.Windows.Input;
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
using Artemis.UI.Shared; using Artemis.UI.Shared;
namespace Artemis.UI.DefaultTypes.DataModel.Input namespace Artemis.UI.DefaultTypes.DataModel.Input

View File

@ -1,4 +1,4 @@
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using SkiaSharp; using SkiaSharp;

View File

@ -1,4 +1,4 @@
using Artemis.Core.DataModelExpansions; using Artemis.Core.Modules;
using Artemis.UI.Shared; using Artemis.UI.Shared;
namespace Artemis.UI.DefaultTypes.DataModel.Input namespace Artemis.UI.DefaultTypes.DataModel.Input

View File

@ -87,7 +87,7 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
protected override void OnClose() protected override void OnClose()
{ {
_updateTimer.Dispose(); _updateTimer.Dispose();
base.OnClose(); base.OnClose();
} }
#endregion #endregion
@ -109,6 +109,9 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
{ {
_updateTimer.Stop(); _updateTimer.Stop();
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed; _updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
MainDataModel?.Dispose();
MainDataModel = null;
_pluginManagementService.PluginFeatureEnabled -= OnPluginFeatureToggled; _pluginManagementService.PluginFeatureEnabled -= OnPluginFeatureToggled;
_pluginManagementService.PluginFeatureDisabled -= OnPluginFeatureToggled; _pluginManagementService.PluginFeatureDisabled -= OnPluginFeatureToggled;