mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Data model - Support all lists implementing IEnumerable<>
This commit is contained in:
parent
17d41647b6
commit
3b6753a0ff
@ -88,11 +88,33 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the default value of the given type
|
/// Returns the default value of the given type
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static object GetDefault(this Type type)
|
public static object GetDefault(this Type type)
|
||||||
{
|
{
|
||||||
return type.IsValueType ? Activator.CreateInstance(type) : null;
|
return type.IsValueType ? Activator.CreateInstance(type) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the given type is a generic enumerable
|
||||||
|
/// </summary>
|
||||||
|
public static bool IsGenericEnumerable(this Type type)
|
||||||
|
{
|
||||||
|
return type.GetInterfaces().Any(x =>
|
||||||
|
x.IsGenericType &&
|
||||||
|
x.GetGenericTypeDefinition() == typeof(IEnumerable<>));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines the type of the provided generic enumerable type
|
||||||
|
/// </summary>
|
||||||
|
public static Type? GetGenericEnumerableType(this Type type)
|
||||||
|
{
|
||||||
|
Type enumerableType = type.GetInterfaces().FirstOrDefault(x =>
|
||||||
|
x.IsGenericType &&
|
||||||
|
x.GetGenericTypeDefinition() == typeof(IEnumerable<>));
|
||||||
|
|
||||||
|
return enumerableType?.GenericTypeArguments[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,7 +93,7 @@ namespace Artemis.Core
|
|||||||
if (ListPath != null)
|
if (ListPath != null)
|
||||||
{
|
{
|
||||||
Type listType = ListPath.GetPropertyType()!;
|
Type listType = ListPath.GetPropertyType()!;
|
||||||
ListType = listType.GetGenericArguments()[0];
|
ListType = listType.GetGenericEnumerableType();
|
||||||
IsPrimitiveList = ListType.IsPrimitive || ListType.IsEnum || ListType == typeof(string);
|
IsPrimitiveList = ListType.IsPrimitive || ListType.IsEnum || ListType == typeof(string);
|
||||||
|
|
||||||
// Create a new root group
|
// Create a new root group
|
||||||
@ -129,10 +129,10 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
if (!Children.Any())
|
if (!Children.Any())
|
||||||
return false;
|
return false;
|
||||||
if (!(target is IList list))
|
if (!(target is IEnumerable enumerable))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
IEnumerable<object> objectList = list.Cast<object>();
|
IEnumerable<object> objectList = enumerable.Cast<object>();
|
||||||
return ListOperator switch
|
return ListOperator switch
|
||||||
{
|
{
|
||||||
ListOperator.Any => objectList.Any(o => Children[0].EvaluateObject(o)),
|
ListOperator.Any => objectList.Any(o => Children[0].EvaluateObject(o)),
|
||||||
@ -180,14 +180,14 @@ namespace Artemis.Core
|
|||||||
DataModelPath listPath = new DataModelPath(null, Entity.ListPath);
|
DataModelPath listPath = new DataModelPath(null, Entity.ListPath);
|
||||||
Type listType = listPath.GetPropertyType()!;
|
Type listType = listPath.GetPropertyType()!;
|
||||||
// Can't check this on an invalid list, if it becomes valid later lets hope for the best
|
// Can't check this on an invalid list, if it becomes valid later lets hope for the best
|
||||||
if (listPath.IsValid && !typeof(IList).IsAssignableFrom(listType))
|
if (listPath.IsValid && !listPath.PointsToList)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ListPath = listPath;
|
ListPath = listPath;
|
||||||
SubscribeToListPath();
|
SubscribeToListPath();
|
||||||
if (ListPath.IsValid)
|
if (ListPath.IsValid)
|
||||||
{
|
{
|
||||||
ListType = listType.GetGenericArguments()[0];
|
ListType = listType.GetGenericEnumerableType();
|
||||||
IsPrimitiveList = ListType.IsPrimitive || ListType.IsEnum || ListType == typeof(string);
|
IsPrimitiveList = ListType.IsPrimitive || ListType.IsEnum || ListType == typeof(string);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@ -114,8 +114,14 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a boolean indicating whether this data model path points to a list
|
/// Gets a boolean indicating whether this data model path points to a list
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool PointsToList => Segments.LastOrDefault()?.GetPropertyType() != null &&
|
public bool PointsToList
|
||||||
typeof(IList).IsAssignableFrom(Segments.LastOrDefault()?.GetPropertyType());
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Type? type = GetPropertyType();
|
||||||
|
return type?.IsGenericEnumerable() ?? false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal DataModelPathEntity Entity { get; }
|
internal DataModelPathEntity Entity { get; }
|
||||||
|
|
||||||
|
|||||||
@ -142,79 +142,6 @@ namespace Artemis.Core.DataModelExpansions
|
|||||||
return value as T;
|
return value as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal bool ContainsPath(string? path)
|
|
||||||
{
|
|
||||||
if (path == null)
|
|
||||||
return false;
|
|
||||||
string[] parts = path.Split('.');
|
|
||||||
Type? current = GetType();
|
|
||||||
foreach (string part in parts)
|
|
||||||
{
|
|
||||||
PropertyInfo? property = current?.GetProperty(part);
|
|
||||||
current = property?.PropertyType;
|
|
||||||
if (property == null)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal Type GetTypeAtPath(string path)
|
|
||||||
{
|
|
||||||
if (!ContainsPath(path))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
string[] parts = path.Split('.');
|
|
||||||
Type current = GetType();
|
|
||||||
|
|
||||||
Type result = null;
|
|
||||||
foreach (string part in parts)
|
|
||||||
{
|
|
||||||
PropertyInfo? property = current.GetProperty(part);
|
|
||||||
current = property.PropertyType;
|
|
||||||
result = property.PropertyType;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal Type GetListTypeInPath(string path)
|
|
||||||
{
|
|
||||||
if (!ContainsPath(path))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
string[] parts = path.Split('.');
|
|
||||||
Type current = GetType();
|
|
||||||
|
|
||||||
int index = 0;
|
|
||||||
foreach (string part in parts)
|
|
||||||
{
|
|
||||||
// Only return a type if the path CONTAINS a list, not if it points TO a list
|
|
||||||
if (index == parts.Length - 1)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
PropertyInfo? property = current.GetProperty(part);
|
|
||||||
|
|
||||||
// For lists, look into the list type instead of the list itself
|
|
||||||
if (typeof(IList).IsAssignableFrom(property.PropertyType))
|
|
||||||
return property.PropertyType.GetGenericArguments()[0];
|
|
||||||
|
|
||||||
current = property.PropertyType;
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal Type GetListTypeAtPath(string path)
|
|
||||||
{
|
|
||||||
if (!ContainsPath(path))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
Type child = GetTypeAtPath(path);
|
|
||||||
return child.GenericTypeArguments.Length > 0 ? child.GenericTypeArguments[0] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Events
|
#region Events
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -9,31 +9,38 @@ namespace Artemis.UI.Shared
|
|||||||
{
|
{
|
||||||
public class DataModelListViewModel : DataModelVisualizationViewModel
|
public class DataModelListViewModel : DataModelVisualizationViewModel
|
||||||
{
|
{
|
||||||
private string _count;
|
private string _countDisplay;
|
||||||
private IList _list;
|
private IEnumerable _list;
|
||||||
|
private int _listCount;
|
||||||
|
|
||||||
internal DataModelListViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, DataModelPath dataModelPath) : base(dataModel, parent, dataModelPath)
|
internal DataModelListViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, DataModelPath dataModelPath) : base(dataModel, parent, dataModelPath)
|
||||||
{
|
{
|
||||||
ListChildren = new BindableCollection<DataModelVisualizationViewModel>();
|
ListChildren = new BindableCollection<DataModelVisualizationViewModel>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IList List
|
public IEnumerable List
|
||||||
{
|
{
|
||||||
get => _list;
|
get => _list;
|
||||||
set => SetAndNotify(ref _list, value);
|
set => SetAndNotify(ref _list, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BindableCollection<DataModelVisualizationViewModel> ListChildren { get; set; }
|
public int ListCount
|
||||||
|
|
||||||
public string Count
|
|
||||||
{
|
{
|
||||||
get => _count;
|
get => _listCount;
|
||||||
set => SetAndNotify(ref _count, value);
|
set => SetAndNotify(ref _listCount, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string CountDisplay
|
||||||
|
{
|
||||||
|
get => _countDisplay;
|
||||||
|
set => SetAndNotify(ref _countDisplay, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BindableCollection<DataModelVisualizationViewModel> ListChildren { get; set; }
|
||||||
|
|
||||||
public DataModelPropertiesViewModel GetListTypeViewModel(IDataModelUIService dataModelUIService)
|
public DataModelPropertiesViewModel GetListTypeViewModel(IDataModelUIService dataModelUIService)
|
||||||
{
|
{
|
||||||
Type listType = DataModelPath.GetPropertyType()?.GenericTypeArguments[0];
|
Type listType = DataModelPath.GetPropertyType()?.GetGenericEnumerableType();
|
||||||
if (listType == null)
|
if (listType == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@ -42,10 +49,8 @@ namespace Artemis.UI.Shared
|
|||||||
viewModel.Update(dataModelUIService);
|
viewModel.Update(dataModelUIService);
|
||||||
|
|
||||||
// Put an empty value into the list type property view model
|
// Put an empty value into the list type property view model
|
||||||
if (viewModel is DataModelListPropertiesViewModel dataModelListClassViewModel)
|
if (viewModel is DataModelListPropertiesViewModel dataModelListClassViewModel) return dataModelListClassViewModel;
|
||||||
{
|
|
||||||
return dataModelListClassViewModel;
|
|
||||||
}
|
|
||||||
if (viewModel is DataModelListPropertyViewModel dataModelListPropertyViewModel)
|
if (viewModel is DataModelListPropertyViewModel dataModelListPropertyViewModel)
|
||||||
{
|
{
|
||||||
dataModelListPropertyViewModel.DisplayValue = Activator.CreateInstance(dataModelListPropertyViewModel.ListType);
|
dataModelListPropertyViewModel.DisplayValue = Activator.CreateInstance(dataModelListPropertyViewModel.ListType);
|
||||||
@ -62,7 +67,7 @@ namespace Artemis.UI.Shared
|
|||||||
if (Parent != null && !Parent.IsVisualizationExpanded)
|
if (Parent != null && !Parent.IsVisualizationExpanded)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
List = GetCurrentValue() as IList;
|
List = GetCurrentValue() as IEnumerable;
|
||||||
if (List == null)
|
if (List == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -79,7 +84,9 @@ namespace Artemis.UI.Shared
|
|||||||
ListChildren.Add(child);
|
ListChildren.Add(child);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
child = ListChildren[index];
|
child = ListChildren[index];
|
||||||
|
}
|
||||||
|
|
||||||
if (child is DataModelListPropertiesViewModel dataModelListClassViewModel)
|
if (child is DataModelListPropertiesViewModel dataModelListClassViewModel)
|
||||||
{
|
{
|
||||||
@ -96,10 +103,18 @@ namespace Artemis.UI.Shared
|
|||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (ListChildren.Count > List.Count)
|
ListCount = index + 1;
|
||||||
|
|
||||||
|
while (ListChildren.Count > ListCount)
|
||||||
ListChildren.RemoveAt(ListChildren.Count - 1);
|
ListChildren.RemoveAt(ListChildren.Count - 1);
|
||||||
|
|
||||||
Count = $"{ListChildren.Count} {(ListChildren.Count == 1 ? "item" : "items")}";
|
CountDisplay = $"{ListChildren.Count} {(ListChildren.Count == 1 ? "item" : "items")}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"[List] {DisplayPath ?? Path} - {ListCount} item(s)";
|
||||||
}
|
}
|
||||||
|
|
||||||
protected DataModelVisualizationViewModel CreateListChild(IDataModelUIService dataModelUIService, Type listType)
|
protected DataModelVisualizationViewModel CreateListChild(IDataModelUIService dataModelUIService, Type listType)
|
||||||
@ -117,11 +132,5 @@ namespace Artemis.UI.Shared
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return $"[List] {DisplayPath ?? Path} - {List?.Count ?? 0} item(s)";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -133,7 +133,9 @@ namespace Artemis.UI.Shared
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (looseMatch)
|
if (looseMatch)
|
||||||
IsMatchingFilteredTypes = filteredTypes.Any(t => t.IsCastableFrom(type) || t == typeof(Enum) && type.IsEnum);
|
IsMatchingFilteredTypes = filteredTypes.Any(t => t.IsCastableFrom(type) ||
|
||||||
|
t == typeof(Enum) && type.IsEnum ||
|
||||||
|
t == typeof(IEnumerable<>) && type.IsGenericEnumerable());
|
||||||
else
|
else
|
||||||
IsMatchingFilteredTypes = filteredTypes.Any(t => t == type || t == typeof(Enum) && type.IsEnum);
|
IsMatchingFilteredTypes = filteredTypes.Any(t => t == type || t == typeof(Enum) && type.IsEnum);
|
||||||
}
|
}
|
||||||
@ -260,7 +262,7 @@ namespace Artemis.UI.Shared
|
|||||||
// For primitives, create a property view model, it may be null that is fine
|
// For primitives, create a property view model, it may be null that is fine
|
||||||
if (propertyType.IsPrimitive || propertyType.IsEnum || propertyType == typeof(string))
|
if (propertyType.IsPrimitive || propertyType.IsEnum || propertyType == typeof(string))
|
||||||
return new DataModelPropertyViewModel(DataModel, this, dataModelPath) {Depth = depth};
|
return new DataModelPropertyViewModel(DataModel, this, dataModelPath) {Depth = depth};
|
||||||
if (typeof(IList).IsAssignableFrom(propertyType))
|
if (propertyType.IsGenericEnumerable())
|
||||||
return new DataModelListViewModel(DataModel, this, dataModelPath) {Depth = depth};
|
return new DataModelListViewModel(DataModel, this, dataModelPath) {Depth = depth};
|
||||||
// For other value types create a child view model
|
// For other value types create a child view model
|
||||||
if (propertyType.IsClass || propertyType.IsStruct())
|
if (propertyType.IsClass || propertyType.IsStruct())
|
||||||
|
|||||||
@ -86,7 +86,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
TargetSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule());
|
TargetSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule());
|
||||||
TargetSelectionViewModel.FilterTypes = new[] {typeof(IList)};
|
TargetSelectionViewModel.FilterTypes = new[] {typeof(IEnumerable<>)};
|
||||||
TargetSelectionViewModel.ButtonBrush = new SolidColorBrush(Color.FromRgb(71, 108, 188));
|
TargetSelectionViewModel.ButtonBrush = new SolidColorBrush(Color.FromRgb(71, 108, 188));
|
||||||
TargetSelectionViewModel.Placeholder = "Select a list";
|
TargetSelectionViewModel.Placeholder = "Select a list";
|
||||||
TargetSelectionViewModel.PropertySelected += TargetSelectionViewModelOnPropertySelected;
|
TargetSelectionViewModel.PropertySelected += TargetSelectionViewModelOnPropertySelected;
|
||||||
|
|||||||
@ -4,7 +4,6 @@ using System.Linq;
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.Core.DataModelExpansions;
|
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
|
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
@ -83,19 +82,6 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
|
|
||||||
public DelegateCommand SelectOperatorCommand { get; }
|
public DelegateCommand SelectOperatorCommand { get; }
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (LeftSideSelectionViewModel != null)
|
|
||||||
{
|
|
||||||
LeftSideSelectionViewModel.PropertySelected -= LeftSideOnPropertySelected;
|
|
||||||
LeftSideSelectionViewModel.Dispose();
|
|
||||||
LeftSideSelectionViewModel = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
DisposeRightSideStatic();
|
|
||||||
DisposeRightSideDynamic();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Delete()
|
public override void Delete()
|
||||||
{
|
{
|
||||||
base.Delete();
|
base.Delete();
|
||||||
@ -239,5 +225,18 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
RightSideSelectionViewModel = null;
|
RightSideSelectionViewModel = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (LeftSideSelectionViewModel != null)
|
||||||
|
{
|
||||||
|
LeftSideSelectionViewModel.PropertySelected -= LeftSideOnPropertySelected;
|
||||||
|
LeftSideSelectionViewModel.Dispose();
|
||||||
|
LeftSideSelectionViewModel = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
DisposeRightSideStatic();
|
||||||
|
DisposeRightSideDynamic();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,12 +69,10 @@
|
|||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock Grid.Column="0" Margin="0 0 5 0" FontWeight="Bold">
|
<TextBlock Grid.Column="0" Margin="0 0 5 0" FontWeight="Bold" Text="[List]"/>
|
||||||
[List]
|
|
||||||
</TextBlock>
|
|
||||||
<TextBlock Grid.Column="1" Text="{Binding PropertyDescription.Name}" ToolTip="{Binding PropertyDescription.Description}" />
|
<TextBlock Grid.Column="1" Text="{Binding PropertyDescription.Name}" ToolTip="{Binding PropertyDescription.Description}" />
|
||||||
<TextBlock Grid.Column="2"
|
<TextBlock Grid.Column="2"
|
||||||
Text="{Binding Count, Mode=OneWay}"
|
Text="{Binding CountDisplay, Mode=OneWay}"
|
||||||
FontFamily="Consolas"
|
FontFamily="Consolas"
|
||||||
HorizontalAlignment="Right" />
|
HorizontalAlignment="Right" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user