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

Conditions - Big refactor to prepare for conditional databindings

Conditions - Improved list-condition functionality
This commit is contained in:
SpoinkyNL 2020-09-20 21:34:27 +02:00
parent a646ff95ed
commit 8aba0a55ec
55 changed files with 1382 additions and 1608 deletions

View File

@ -62,7 +62,7 @@ namespace Artemis.Core
internal abstract bool EvaluateObject(object target); internal abstract bool EvaluateObject(object target);
internal abstract void Save(); internal abstract void Save();
internal abstract DisplayConditionPartEntity GetEntity(); internal abstract DataModelConditionPartEntity GetEntity();
#region IDisposable #region IDisposable

View File

@ -20,7 +20,7 @@ namespace Artemis.Core
public DataModelConditionGroup(DataModelConditionPart parent) public DataModelConditionGroup(DataModelConditionPart parent)
{ {
Parent = parent; Parent = parent;
Entity = new DisplayConditionGroupEntity(); Entity = new DataModelConditionGroupEntity();
} }
/// <summary> /// <summary>
@ -28,7 +28,7 @@ namespace Artemis.Core
/// </summary> /// </summary>
/// <param name="parent"></param> /// <param name="parent"></param>
/// <param name="entity"></param> /// <param name="entity"></param>
public DataModelConditionGroup(DataModelConditionPart parent, DisplayConditionGroupEntity entity) public DataModelConditionGroup(DataModelConditionPart parent, DataModelConditionGroupEntity entity)
{ {
Parent = parent; Parent = parent;
Entity = entity; Entity = entity;
@ -36,13 +36,13 @@ namespace Artemis.Core
foreach (var childEntity in Entity.Children) foreach (var childEntity in Entity.Children)
{ {
if (childEntity is DisplayConditionGroupEntity groupEntity) if (childEntity is DataModelConditionGroupEntity groupEntity)
AddChild(new DataModelConditionGroup(this, groupEntity)); AddChild(new DataModelConditionGroup(this, groupEntity));
else if (childEntity is DisplayConditionListEntity listEntity) else if (childEntity is DataModelConditionListEntity listEntity)
AddChild(new DataModelConditionList(this, listEntity)); AddChild(new DataModelConditionList(this, listEntity));
else if (childEntity is DisplayConditionPredicateEntity predicateEntity) else if (childEntity is DataModelConditionPredicateEntity predicateEntity)
AddChild(new DataModelConditionPredicate(this, predicateEntity)); AddChild(new DataModelConditionPredicate(this, predicateEntity));
else if (childEntity is DisplayConditionListPredicateEntity listPredicateEntity) else if (childEntity is DataModelConditionListPredicateEntity listPredicateEntity)
AddChild(new DataModelConditionListPredicate(this, listPredicateEntity)); AddChild(new DataModelConditionListPredicate(this, listPredicateEntity));
} }
} }
@ -52,13 +52,13 @@ namespace Artemis.Core
/// </summary> /// </summary>
public BooleanOperator BooleanOperator { get; set; } public BooleanOperator BooleanOperator { get; set; }
internal DisplayConditionGroupEntity Entity { get; set; } internal DataModelConditionGroupEntity Entity { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public override bool Evaluate() public override bool Evaluate()
{ {
if (_disposed) if (_disposed)
throw new ObjectDisposedException("DisplayConditionGroup"); throw new ObjectDisposedException("DataModelConditionGroup");
// Empty groups are always true // Empty groups are always true
if (Children.Count == 0) if (Children.Count == 0)
@ -100,7 +100,7 @@ namespace Artemis.Core
internal override bool EvaluateObject(object target) internal override bool EvaluateObject(object target)
{ {
if (_disposed) if (_disposed)
throw new ObjectDisposedException("DisplayConditionGroup"); throw new ObjectDisposedException("DataModelConditionGroup");
// Empty groups are always true // Empty groups are always true
if (Children.Count == 0) if (Children.Count == 0)
@ -129,7 +129,7 @@ namespace Artemis.Core
child.Save(); child.Save();
} }
internal override DisplayConditionPartEntity GetEntity() internal override DataModelConditionPartEntity GetEntity()
{ {
return Entity; return Entity;
} }

View File

@ -22,12 +22,12 @@ namespace Artemis.Core
public DataModelConditionList(DataModelConditionPart parent) public DataModelConditionList(DataModelConditionPart parent)
{ {
Parent = parent; Parent = parent;
Entity = new DisplayConditionListEntity(); Entity = new DataModelConditionListEntity();
Initialize(); Initialize();
} }
internal DataModelConditionList(DataModelConditionPart parent, DisplayConditionListEntity entity) internal DataModelConditionList(DataModelConditionPart parent, DataModelConditionListEntity entity)
{ {
Parent = parent; Parent = parent;
Entity = entity; Entity = entity;
@ -36,7 +36,7 @@ namespace Artemis.Core
Initialize(); Initialize();
} }
internal DisplayConditionListEntity Entity { get; set; } internal DataModelConditionListEntity Entity { get; set; }
/// <summary> /// <summary>
/// Gets or sets the list operator /// Gets or sets the list operator
@ -62,7 +62,7 @@ namespace Artemis.Core
public override bool Evaluate() public override bool Evaluate()
{ {
if (_disposed) if (_disposed)
throw new ObjectDisposedException("DisplayConditionList"); throw new ObjectDisposedException("DataModelConditionList");
if (CompiledListAccessor == null) if (CompiledListAccessor == null)
return false; return false;
@ -78,7 +78,7 @@ namespace Artemis.Core
public void UpdateList(DataModel dataModel, string path) public void UpdateList(DataModel dataModel, string path)
{ {
if (_disposed) if (_disposed)
throw new ObjectDisposedException("DisplayConditionList"); throw new ObjectDisposedException("DataModelConditionList");
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");
@ -129,7 +129,7 @@ namespace Artemis.Core
internal override bool EvaluateObject(object target) internal override bool EvaluateObject(object target)
{ {
if (_disposed) if (_disposed)
throw new ObjectDisposedException("DisplayConditionList"); throw new ObjectDisposedException("DataModelConditionList");
if (!Children.Any()) if (!Children.Any())
return false; return false;
@ -166,7 +166,7 @@ namespace Artemis.Core
child.Save(); child.Save();
} }
internal override DisplayConditionPartEntity GetEntity() internal override DataModelConditionPartEntity GetEntity()
{ {
return Entity; return Entity;
} }
@ -189,7 +189,7 @@ namespace Artemis.Core
CreateExpression(); CreateExpression();
// There should only be one child and it should be a group // There should only be one child and it should be a group
if (Entity.Children.SingleOrDefault() is DisplayConditionGroupEntity rootGroup) if (Entity.Children.SingleOrDefault() is DataModelConditionGroupEntity rootGroup)
AddChild(new DataModelConditionGroup(this, rootGroup)); AddChild(new DataModelConditionGroup(this, rootGroup));
else else
{ {
@ -201,12 +201,14 @@ namespace Artemis.Core
private void CreateExpression() private void CreateExpression()
{ {
if (_disposed) if (_disposed)
throw new ObjectDisposedException("DisplayConditionList"); throw new ObjectDisposedException("DataModelConditionList");
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()),
(expression, s) => Expression.Convert(Expression.Property(expression, s), typeof(IList))); Expression.Property
);
accessor = Expression.Convert(accessor, typeof(IList));
var lambda = Expression.Lambda<Func<object, IList>>(accessor, parameter); var lambda = Expression.Lambda<Func<object, IList>>(accessor, parameter);
CompiledListAccessor = lambda.Compile(); CompiledListAccessor = lambda.Compile();

View File

@ -18,32 +18,32 @@ namespace Artemis.Core
/// </summary> /// </summary>
/// <param name="parent"></param> /// <param name="parent"></param>
/// <param name="predicateType"></param> /// <param name="predicateType"></param>
public DataModelConditionListPredicate(DataModelConditionPart parent, ProfileRightSideType predicateType) public DataModelConditionListPredicate(DataModelConditionPart parent, ListRightSideType predicateType)
{ {
Parent = parent; Parent = parent;
PredicateType = predicateType; PredicateType = predicateType;
Entity = new DisplayConditionListPredicateEntity(); Entity = new DataModelConditionListPredicateEntity();
ApplyParentList(); ApplyParentList();
Initialize(); Initialize();
} }
internal DataModelConditionListPredicate(DataModelConditionPart parent, DisplayConditionListPredicateEntity entity) internal DataModelConditionListPredicate(DataModelConditionPart parent, DataModelConditionListPredicateEntity entity)
{ {
Parent = parent; Parent = parent;
Entity = entity; Entity = entity;
PredicateType = (ProfileRightSideType) entity.PredicateType; PredicateType = (ListRightSideType) entity.PredicateType;
ApplyParentList(); ApplyParentList();
Initialize(); Initialize();
} }
internal DisplayConditionListPredicateEntity Entity { get; set; } internal DataModelConditionListPredicateEntity Entity { get; set; }
/// <summary> /// <summary>
/// Gets or sets the predicate type /// Gets or sets the predicate type
/// </summary> /// </summary>
public ProfileRightSideType PredicateType { get; set; } public ListRightSideType PredicateType { get; set; }
/// <summary> /// <summary>
/// Gets the operator /// Gets the operator
@ -55,6 +55,11 @@ namespace Artemis.Core
/// </summary> /// </summary>
public Type ListType { get; private set; } public Type ListType { get; private set; }
/// <summary>
/// Gets whether the list contains primitives
/// </summary>
public bool IsPrimitiveList { get; private set; }
/// <summary> /// <summary>
/// Gets the currently used instance of the list data model /// Gets the currently used instance of the list data model
/// </summary> /// </summary>
@ -70,6 +75,12 @@ namespace Artemis.Core
/// </summary> /// </summary>
public string LeftPropertyPath { get; private set; } public string LeftPropertyPath { get; private set; }
/// <summary>
/// Gets the currently used instance of the right side data model
/// <para>Note: This is null when using a path inside the list</para>
/// </summary>
public DataModel RightDataModel { get; set; }
/// <summary> /// <summary>
/// Gets the path of the right property in the <see cref="ListType" /> /// Gets the path of the right property in the <see cref="ListType" />
/// </summary> /// </summary>
@ -77,21 +88,28 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Gets the right static value, only used it <see cref="PredicateType" /> is /// Gets the right static value, only used it <see cref="PredicateType" /> is
/// <see cref="ProfileRightSideType.Static" /> /// <see cref="ListRightSideType.Static" />
/// </summary> /// </summary>
public object RightStaticValue { get; private set; } public object RightStaticValue { get; private set; }
/// <summary> /// <summary>
/// Gets the compiled function that evaluates this predicate /// Gets the compiled expression that evaluates this predicate
/// </summary> /// </summary>
public Func<object, bool> CompiledListPredicate { get; private set; } public Func<object, bool> CompiledListPredicate { get; private set; }
/// <summary>
/// Gets the compiled expression that evaluates this predicate on an external right-side data model
/// </summary>
public Func<object, DataModel, bool> CompiledExternalListPredicate { get; set; }
/// <summary> /// <summary>
/// Updates the left side of the predicate /// Updates the left side of the predicate
/// </summary> /// </summary>
/// <param name="path">The path pointing to the left side value inside the list</param> /// <param name="path">The path pointing to the left side value inside the list</param>
public void UpdateLeftSide(string path) public void UpdateLeftSide(string path)
{ {
if (IsPrimitiveList)
throw new ArtemisCoreException("Cannot apply a left side to a predicate inside a primitive list");
if (!ListContainsInnerPath(path)) if (!ListContainsInnerPath(path))
throw new ArtemisCoreException($"List type {ListType.Name} does not contain path {path}"); throw new ArtemisCoreException($"List type {ListType.Name} does not contain path {path}");
@ -103,7 +121,8 @@ namespace Artemis.Core
} }
/// <summary> /// <summary>
/// Updates the right side of the predicate, makes the predicate dynamic and re-compiles the expression /// Updates the right side of the predicate using a path to a value inside the list item, makes the predicate dynamic
/// and re-compiles the expression
/// </summary> /// </summary>
/// <param name="path">The path pointing to the right side value inside the list</param> /// <param name="path">The path pointing to the right side value inside the list</param>
public void UpdateRightSideDynamic(string path) public void UpdateRightSideDynamic(string path)
@ -111,7 +130,33 @@ namespace Artemis.Core
if (!ListContainsInnerPath(path)) if (!ListContainsInnerPath(path))
throw new ArtemisCoreException($"List type {ListType.Name} does not contain path {path}"); throw new ArtemisCoreException($"List type {ListType.Name} does not contain path {path}");
PredicateType = ProfileRightSideType.Dynamic; PredicateType = ListRightSideType.Dynamic;
RightPropertyPath = path;
CreateExpression();
}
/// <summary>
/// Updates the right side of the predicate using path to a value in a data model, makes the predicate dynamic and
/// re-compiles the expression
/// </summary>
/// <param name="dataModel"></param>
/// <param name="path">The path pointing to the right side value inside the list</param>
public void UpdateRightSideDynamic(DataModel dataModel, string path)
{
if (dataModel != null && path == null)
throw new ArtemisCoreException("If a data model is provided, a path is also required");
if (dataModel == null && path != null)
throw new ArtemisCoreException("If path is provided, a data model is also required");
if (dataModel != null)
{
if (!dataModel.ContainsPath(path))
throw new ArtemisCoreException($"Data model of type {dataModel.GetType().Name} does not contain a property at path '{path}'");
}
PredicateType = ListRightSideType.DynamicExternal;
RightDataModel = dataModel;
RightPropertyPath = path; RightPropertyPath = path;
CreateExpression(); CreateExpression();
@ -123,7 +168,7 @@ namespace Artemis.Core
/// <param name="staticValue">The right side value to use</param> /// <param name="staticValue">The right side value to use</param>
public void UpdateRightSideStatic(object staticValue) public void UpdateRightSideStatic(object staticValue)
{ {
PredicateType = ProfileRightSideType.Static; PredicateType = ListRightSideType.Static;
RightPropertyPath = null; RightPropertyPath = null;
SetStaticValue(staticValue); SetStaticValue(staticValue);
@ -163,11 +208,36 @@ namespace Artemis.Core
return false; return false;
} }
/// <summary>
/// Determines whether the provided path is contained inside the list
/// </summary>
/// <param name="path">The path to evaluate</param>
public bool ListContainsInnerPath(string path)
{
if (ListType == null)
return false;
var parts = path.Split('.');
var current = ListType;
foreach (var part in parts)
{
var property = current.GetProperty(part);
current = property?.PropertyType;
if (property == null)
return false;
}
return true;
}
#region IDisposable #region IDisposable
/// <inheritdoc /> /// <inheritdoc />
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded;
DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved;
ConditionOperatorStore.ConditionOperatorAdded -= ConditionOperatorStoreOnConditionOperatorAdded; ConditionOperatorStore.ConditionOperatorAdded -= ConditionOperatorStoreOnConditionOperatorAdded;
ConditionOperatorStore.ConditionOperatorRemoved -= ConditionOperatorStoreOnConditionOperatorRemoved; ConditionOperatorStore.ConditionOperatorRemoved -= ConditionOperatorStoreOnConditionOperatorRemoved;
@ -205,9 +275,14 @@ namespace Artemis.Core
throw new ArtemisCoreException($"Data model of type {dataModel.GetType().Name} does not contain a list at path '{path}'"); throw new ArtemisCoreException($"Data model of type {dataModel.GetType().Name} does not contain a list at path '{path}'");
ListType = listType; ListType = listType;
IsPrimitiveList = listType.IsPrimitive || listType.IsEnum || listType == typeof(string);
} }
else else
{
ListType = null; ListType = null;
IsPrimitiveList = true;
}
ListDataModel = dataModel; ListDataModel = dataModel;
ListPropertyPath = path; ListPropertyPath = path;
@ -220,6 +295,7 @@ namespace Artemis.Core
CreateExpression(); CreateExpression();
} }
/// <summary> /// <summary>
/// Evaluates the condition part on the given target /// Evaluates the condition part on the given target
/// </summary> /// </summary>
@ -229,26 +305,14 @@ namespace Artemis.Core
/// </param> /// </param>
internal override bool EvaluateObject(object target) internal override bool EvaluateObject(object target)
{ {
return CompiledListPredicate != null && CompiledListPredicate(target); if (PredicateType == ListRightSideType.Static && CompiledListPredicate != null)
} return CompiledListPredicate(target);
if (PredicateType == ListRightSideType.Dynamic && CompiledListPredicate != null)
return CompiledListPredicate(target);
if (PredicateType == ListRightSideType.DynamicExternal && CompiledExternalListPredicate != null)
return CompiledExternalListPredicate(target, RightDataModel);
internal bool ListContainsInnerPath(string path) return false;
{
if (ListType == null)
return false;
var parts = path.Split('.');
var current = ListType;
foreach (var part in parts)
{
var property = current.GetProperty(part);
current = property?.PropertyType;
if (property == null)
return false;
}
return true;
} }
internal Type GetTypeAtInnerPath(string path) internal Type GetTypeAtInnerPath(string path)
@ -280,6 +344,8 @@ namespace Artemis.Core
} }
Entity.LeftPropertyPath = LeftPropertyPath; Entity.LeftPropertyPath = LeftPropertyPath;
Entity.RightDataModelGuid = RightDataModel?.PluginInfo?.Guid;
Entity.RightPropertyPath = RightPropertyPath; Entity.RightPropertyPath = RightPropertyPath;
Entity.RightStaticValue = JsonConvert.SerializeObject(RightStaticValue); Entity.RightStaticValue = JsonConvert.SerializeObject(RightStaticValue);
@ -290,13 +356,15 @@ namespace Artemis.Core
} }
} }
internal override DisplayConditionPartEntity GetEntity() internal override DataModelConditionPartEntity GetEntity()
{ {
return Entity; return Entity;
} }
private void Initialize() private void Initialize()
{ {
DataModelStore.DataModelAdded += DataModelStoreOnDataModelAdded;
DataModelStore.DataModelRemoved += DataModelStoreOnDataModelRemoved;
ConditionOperatorStore.ConditionOperatorAdded += ConditionOperatorStoreOnConditionOperatorAdded; ConditionOperatorStore.ConditionOperatorAdded += ConditionOperatorStoreOnConditionOperatorAdded;
ConditionOperatorStore.ConditionOperatorRemoved += ConditionOperatorStoreOnConditionOperatorRemoved; ConditionOperatorStore.ConditionOperatorRemoved += ConditionOperatorStoreOnConditionOperatorRemoved;
@ -313,13 +381,20 @@ namespace Artemis.Core
} }
// Right side dynamic // Right side dynamic
if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightPropertyPath != null) if (PredicateType == ListRightSideType.Dynamic && Entity.RightPropertyPath != null)
{ {
if (ListContainsInnerPath(Entity.RightPropertyPath)) if (ListContainsInnerPath(Entity.RightPropertyPath))
UpdateRightSideDynamic(Entity.RightPropertyPath); UpdateRightSideDynamic(Entity.RightPropertyPath);
} }
// Right side dynamic using an external data model
else if (PredicateType == ListRightSideType.Dynamic && Entity.RightDataModelGuid != null && Entity.RightPropertyPath != null)
{
var dataModel = DataModelStore.Get(Entity.RightDataModelGuid.Value)?.DataModel;
if (dataModel != null && dataModel.ContainsPath(Entity.RightPropertyPath))
UpdateRightSideDynamic(dataModel, Entity.RightPropertyPath);
}
// Right side static // Right side static
else if (PredicateType == ProfileRightSideType.Static && Entity.RightStaticValue != null) else if (PredicateType == ListRightSideType.Static && Entity.RightStaticValue != null)
{ {
try try
{ {
@ -363,10 +438,12 @@ namespace Artemis.Core
return; return;
// If the operator does not support a right side, create a static expression because the right side will simply be null // If the operator does not support a right side, create a static expression because the right side will simply be null
if (PredicateType == ProfileRightSideType.Dynamic && Operator.SupportsRightSide) if (PredicateType == ListRightSideType.Dynamic && Operator.SupportsRightSide)
CreateDynamicExpression(); CreateDynamicExpression();
else if (PredicateType == ListRightSideType.DynamicExternal && Operator.SupportsRightSide)
CreateStaticExpression(); CreateDynamicExternalExpression();
else
CreateStaticExpression();
} }
private void ValidateOperator() private void ValidateOperator()
@ -382,7 +459,7 @@ namespace Artemis.Core
private void ValidateRightSide() private void ValidateRightSide()
{ {
var leftSideType = GetTypeAtInnerPath(LeftPropertyPath); var leftSideType = GetTypeAtInnerPath(LeftPropertyPath);
if (PredicateType == ProfileRightSideType.Dynamic) if (PredicateType == ListRightSideType.Dynamic)
{ {
if (RightPropertyPath == null) if (RightPropertyPath == null)
return; return;
@ -391,6 +468,15 @@ namespace Artemis.Core
if (!leftSideType.IsCastableFrom(rightSideType)) if (!leftSideType.IsCastableFrom(rightSideType))
UpdateRightSideDynamic(null); UpdateRightSideDynamic(null);
} }
else if (PredicateType == ListRightSideType.DynamicExternal)
{
if (RightDataModel == null)
return;
var rightSideType = RightDataModel.GetTypeAtPath(RightPropertyPath);
if (!leftSideType.IsCastableFrom(rightSideType))
UpdateRightSideDynamic(null, null);
}
else else
{ {
if (RightStaticValue != null && leftSideType.IsCastableFrom(RightStaticValue.GetType())) if (RightStaticValue != null && leftSideType.IsCastableFrom(RightStaticValue.GetType()))
@ -434,7 +520,7 @@ namespace Artemis.Core
var rightSideAccessor = CreateListAccessor(RightPropertyPath, leftSideParameter); var rightSideAccessor = CreateListAccessor(RightPropertyPath, leftSideParameter);
// A conversion may be required if the types differ // A conversion may be required if the types differ
// This can cause issues if the DisplayConditionOperator wasn't accurate in it's supported types but that is not a concern here // This can cause issues if the DataModelConditionOperator wasn't accurate in it's supported types but that is not a concern here
if (rightSideAccessor.Type != leftSideAccessor.Type) if (rightSideAccessor.Type != leftSideAccessor.Type)
rightSideAccessor = Expression.Convert(rightSideAccessor, leftSideAccessor.Type); rightSideAccessor = Expression.Convert(rightSideAccessor, leftSideAccessor.Type);
@ -443,14 +529,34 @@ namespace Artemis.Core
CompiledListPredicate = lambda.Compile(); CompiledListPredicate = lambda.Compile();
} }
private void CreateStaticExpression() private void CreateDynamicExternalExpression()
{ {
if (LeftPropertyPath == null || Operator == null) if (LeftPropertyPath == null || RightPropertyPath == null || RightDataModel == null || Operator == null)
return; return;
// List accessors share the same parameter because a list always contains one item per entry // List accessors share the same parameter because a list always contains one item per entry
var leftSideParameter = Expression.Parameter(typeof(object), "listItem"); var leftSideParameter = Expression.Parameter(typeof(object), "listItem");
var leftSideAccessor = CreateListAccessor(LeftPropertyPath, leftSideParameter); var leftSideAccessor = CreateListAccessor(LeftPropertyPath, leftSideParameter);
var rightSideAccessor = CreateAccessor(RightDataModel, RightPropertyPath, "right", out var rightSideParameter);
// A conversion may be required if the types differ
// This can cause issues if the DataModelConditionOperator wasn't accurate in it's supported types but that is not a concern here
if (rightSideAccessor.Type != leftSideAccessor.Type)
rightSideAccessor = Expression.Convert(rightSideAccessor, leftSideAccessor.Type);
var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideAccessor);
var lambda = Expression.Lambda<Func<object, DataModel, bool>>(conditionExpression, leftSideParameter, rightSideParameter);
CompiledExternalListPredicate = lambda.Compile();
}
private void CreateStaticExpression()
{
if (!IsPrimitiveList && LeftPropertyPath == null || Operator == null)
return;
// List accessors share the same parameter because a list always contains one item per entry
var leftSideParameter = Expression.Parameter(typeof(object), "listItem");
var leftSideAccessor = IsPrimitiveList ? Expression.Convert(leftSideParameter, ListType) : CreateListAccessor(LeftPropertyPath, leftSideParameter);
// If the left side is a value type but the input is empty, this isn't a valid expression // If the left side is a value type but the input is empty, this isn't a valid expression
if (leftSideAccessor.Type.IsValueType && RightStaticValue == null) if (leftSideAccessor.Type.IsValueType && RightStaticValue == null)
@ -458,7 +564,7 @@ namespace Artemis.Core
// If the right side value is null, the constant type cannot be inferred and must be provided manually // If the right side value is null, the constant type cannot be inferred and must be provided manually
var rightSideConstant = RightStaticValue != null var rightSideConstant = RightStaticValue != null
? Expression.Constant(RightStaticValue) ? Expression.Constant(Convert.ChangeType(RightStaticValue, leftSideAccessor.Type))
: Expression.Constant(null, leftSideAccessor.Type); : Expression.Constant(null, leftSideAccessor.Type);
var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideConstant); var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideConstant);
@ -474,8 +580,46 @@ namespace Artemis.Core
); );
} }
private Expression CreateAccessor(DataModel dataModel, string path, string parameterName, out ParameterExpression parameter)
{
var listType = dataModel.GetListTypeInPath(path);
if (listType != null)
throw new ArtemisCoreException($"Cannot create a regular accessor at path {path} because the path contains a list");
parameter = Expression.Parameter(typeof(object), parameterName + "DataModel");
return path.Split('.').Aggregate<string, Expression>(
Expression.Convert(parameter, dataModel.GetType()), // Cast to the appropriate type
Expression.Property
);
}
#region Event handlers #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))
UpdateList(dataModel, Entity.LeftPropertyPath);
if (dataModel.PluginInfo.Guid == Entity.RightDataModelGuid && dataModel.ContainsPath(Entity.RightPropertyPath))
UpdateRightSideDynamic(dataModel, Entity.RightPropertyPath);
}
private void DataModelStoreOnDataModelRemoved(object sender, DataModelStoreEvent e)
{
if (ListDataModel == e.Registration.DataModel)
{
CompiledListPredicate = null;
CompiledExternalListPredicate = null;
ListDataModel = null;
}
if (RightDataModel == e.Registration.DataModel)
{
CompiledExternalListPredicate = null;
RightDataModel = null;
}
}
private void ConditionOperatorStoreOnConditionOperatorAdded(object sender, ConditionOperatorStoreEvent e) private void ConditionOperatorStoreOnConditionOperatorAdded(object sender, ConditionOperatorStoreEvent e)
{ {
var conditionOperator = e.Registration.ConditionOperator; var conditionOperator = e.Registration.ConditionOperator;
@ -493,4 +637,25 @@ namespace Artemis.Core
#endregion #endregion
} }
/// <summary>
/// An enum defining the right side type of a profile entity
/// </summary>
public enum ListRightSideType
{
/// <summary>
/// A static right side value
/// </summary>
Static,
/// <summary>
/// A dynamic right side value based on a path in the list
/// </summary>
Dynamic,
/// <summary>
/// A dynamic right side value based on a path in a data model
/// </summary>
DynamicExternal
}
} }

View File

@ -23,12 +23,12 @@ namespace Artemis.Core
{ {
Parent = parent; Parent = parent;
PredicateType = predicateType; PredicateType = predicateType;
Entity = new DisplayConditionPredicateEntity(); Entity = new DataModelConditionPredicateEntity();
Initialize(); Initialize();
} }
internal DataModelConditionPredicate(DataModelConditionPart parent, DisplayConditionPredicateEntity entity) internal DataModelConditionPredicate(DataModelConditionPart parent, DataModelConditionPredicateEntity entity)
{ {
Parent = parent; Parent = parent;
Entity = entity; Entity = entity;
@ -83,7 +83,7 @@ namespace Artemis.Core
/// </summary> /// </summary>
public Func<DataModel, bool> CompiledStaticPredicate { get; private set; } public Func<DataModel, bool> CompiledStaticPredicate { get; private set; }
internal DisplayConditionPredicateEntity Entity { get; set; } internal DataModelConditionPredicateEntity Entity { get; set; }
/// <summary> /// <summary>
/// Updates the left side of the predicate /// Updates the left side of the predicate
@ -309,7 +309,7 @@ namespace Artemis.Core
} }
} }
internal override DisplayConditionPartEntity GetEntity() internal override DataModelConditionPartEntity GetEntity()
{ {
return Entity; return Entity;
} }
@ -369,7 +369,7 @@ namespace Artemis.Core
var rightSideAccessor = CreateAccessor(RightDataModel, RightPropertyPath, "right", out var rightSideParameter); var rightSideAccessor = CreateAccessor(RightDataModel, RightPropertyPath, "right", out var rightSideParameter);
// A conversion may be required if the types differ // A conversion may be required if the types differ
// This can cause issues if the DisplayConditionOperator wasn't accurate in it's supported types but that is not a concern here // This can cause issues if the DataModelConditionOperator wasn't accurate in it's supported types but that is not a concern here
if (rightSideAccessor.Type != leftSideAccessor.Type) if (rightSideAccessor.Type != leftSideAccessor.Type)
rightSideAccessor = Expression.Convert(rightSideAccessor, leftSideAccessor.Type); rightSideAccessor = Expression.Convert(rightSideAccessor, leftSideAccessor.Type);
@ -391,7 +391,7 @@ namespace Artemis.Core
// If the right side value is null, the constant type cannot be inferred and must be provided manually // If the right side value is null, the constant type cannot be inferred and must be provided manually
var rightSideConstant = RightStaticValue != null var rightSideConstant = RightStaticValue != null
? Expression.Constant(RightStaticValue) ? Expression.Constant(Convert.ChangeType(RightStaticValue, leftSideAccessor.Type))
: Expression.Constant(null, leftSideAccessor.Type); : Expression.Constant(null, leftSideAccessor.Type);
var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideConstant); var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideConstant);

View File

@ -72,6 +72,9 @@ namespace Artemis.Core
{ {
_disposed = true; _disposed = true;
DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded;
DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved;
foreach (var dataBindingModifier in Modifiers) foreach (var dataBindingModifier in Modifiers)
dataBindingModifier.Dispose(); dataBindingModifier.Dispose();
} }
@ -154,7 +157,6 @@ namespace Artemis.Core
return SourceDataModel?.GetTypeAtPath(SourcePropertyPath); return SourceDataModel?.GetTypeAtPath(SourcePropertyPath);
} }
/// <summary> /// <summary>
/// Updates the source of the data binding and re-compiles the expression /// Updates the source of the data binding and re-compiles the expression
/// </summary> /// </summary>

View File

@ -49,8 +49,8 @@ namespace Artemis.Core
DisplayContinuously = RenderElementEntity.DisplayContinuously; DisplayContinuously = RenderElementEntity.DisplayContinuously;
AlwaysFinishTimeline = RenderElementEntity.AlwaysFinishTimeline; AlwaysFinishTimeline = RenderElementEntity.AlwaysFinishTimeline;
DataModelConditionGroup = RenderElementEntity.RootDisplayCondition != null DisplayCondition = RenderElementEntity.DisplayCondition != null
? new DataModelConditionGroup(null, RenderElementEntity.RootDisplayCondition) ? new DataModelConditionGroup(null, RenderElementEntity.DisplayCondition)
: new DataModelConditionGroup(null); : new DataModelConditionGroup(null);
ActivateEffects(); ActivateEffects();
@ -82,8 +82,8 @@ namespace Artemis.Core
} }
// Conditions // Conditions
RenderElementEntity.RootDisplayCondition = DataModelConditionGroup?.Entity; RenderElementEntity.DisplayCondition = DisplayCondition?.Entity;
DataModelConditionGroup?.Save(); DisplayCondition?.Save();
} }
#region Properties #region Properties
@ -392,22 +392,22 @@ namespace Artemis.Core
private set => SetAndNotify(ref _displayConditionMet, value); private set => SetAndNotify(ref _displayConditionMet, value);
} }
private DataModelConditionGroup _dataModelConditionGroup; private DataModelConditionGroup _displayCondition;
private TimeSpan _timelinePosition; private TimeSpan _timelinePosition;
private bool _displayConditionMet; private bool _displayConditionMet;
/// <summary> /// <summary>
/// Gets or sets the root display condition group /// Gets or sets the root display condition group
/// </summary> /// </summary>
public DataModelConditionGroup DataModelConditionGroup public DataModelConditionGroup DisplayCondition
{ {
get => _dataModelConditionGroup; get => _displayCondition;
set => SetAndNotify(ref _dataModelConditionGroup, value); set => SetAndNotify(ref _displayCondition, value);
} }
public void UpdateDisplayCondition() public void UpdateDisplayCondition()
{ {
var conditionMet = DataModelConditionGroup == null || DataModelConditionGroup.Evaluate(); var conditionMet = DisplayCondition == null || DisplayCondition.Evaluate();
if (conditionMet && !DisplayConditionMet) if (conditionMet && !DisplayConditionMet)
TimelinePosition = TimeSpan.Zero; TimelinePosition = TimeSpan.Zero;

View File

@ -11,9 +11,9 @@ namespace Artemis.Core.Services
public DataModelService(IPluginService pluginService) public DataModelService(IPluginService pluginService)
{ {
// Add data models of already loaded plugins // Add data models of already loaded plugins
foreach (var module in pluginService.GetPluginsOfType<Module>()) foreach (var module in pluginService.GetPluginsOfType<Module>().Where(p => p.Enabled))
AddModuleDataModel(module); AddModuleDataModel(module);
foreach (var dataModelExpansion in pluginService.GetPluginsOfType<BaseDataModelExpansion>()) foreach (var dataModelExpansion in pluginService.GetPluginsOfType<BaseDataModelExpansion>().Where(p => p.Enabled))
AddDataModelExpansionDataModel(dataModelExpansion); AddDataModelExpansionDataModel(dataModelExpansion);
// Add data models of new plugins when they get enabled // Add data models of new plugins when they get enabled

View File

@ -19,7 +19,7 @@ namespace Artemis.Core.Services
} }
/// <summary> /// <summary>
/// <para>A wrapper around plugin settings for internal use.</para> /// <para>A wrapper around plugin settings for miscellaneous use outside plugins</para>
/// <para>Do not inject into a plugin, for plugins inject <see cref="PluginSettings" /> instead.</para> /// <para>Do not inject into a plugin, for plugins inject <see cref="PluginSettings" /> instead.</para>
/// </summary> /// </summary>
public interface ISettingsService : IProtectedArtemisService public interface ISettingsService : IProtectedArtemisService

View File

@ -56,13 +56,13 @@ namespace Artemis.Core
var candidates = Registrations.Where(r => r.ConditionOperator.CompatibleTypes.Any(t => t.IsCastableFrom(type))).ToList(); var candidates = Registrations.Where(r => r.ConditionOperator.CompatibleTypes.Any(t => t.IsCastableFrom(type))).ToList();
// If there are multiple operators with the same description, use the closest match // If there are multiple operators with the same description, use the closest match
foreach (var displayConditionOperators in candidates.GroupBy(r => r.ConditionOperator.Description).Where(g => g.Count() > 1).ToList()) foreach (var candidate in candidates.GroupBy(r => r.ConditionOperator.Description).Where(g => g.Count() > 1).ToList())
{ {
var closest = displayConditionOperators.OrderByDescending(r => r.ConditionOperator.CompatibleTypes.Contains(type)).FirstOrDefault(); var closest = candidate.OrderByDescending(r => r.ConditionOperator.CompatibleTypes.Contains(type)).FirstOrDefault();
foreach (var displayConditionOperator in displayConditionOperators) foreach (var conditionOperator in candidate)
{ {
if (displayConditionOperator != closest) if (conditionOperator != closest)
candidates.Remove(displayConditionOperator); candidates.Remove(conditionOperator);
} }
} }

View File

@ -9,6 +9,9 @@ namespace Artemis.Plugins.DataModelExpansions.TestData.DataModels
public PluginDataModel() public PluginDataModel()
{ {
PluginSubDataModel = new PluginSubDataModel(); PluginSubDataModel = new PluginSubDataModel();
ListItems = new List<SomeListItem>();
for (var i = 0; i < 20; i++)
ListItems.Add(new SomeListItem {ItemName = $"Item {i + 1}", Number = i});
} }
// Your datamodel can have regular properties and you can annotate them if you'd like // Your datamodel can have regular properties and you can annotate them if you'd like
@ -24,6 +27,14 @@ namespace Artemis.Plugins.DataModelExpansions.TestData.DataModels
public Team Team { get; set; } public Team Team { get; set; }
public bool IsWinning { get; set; } public bool IsWinning { get; set; }
public List<SomeListItem> ListItems { get; set; }
}
public class SomeListItem
{
public string ItemName { get; set; }
public int Number { get; set; }
} }
public enum Team public enum Team
@ -36,7 +47,7 @@ namespace Artemis.Plugins.DataModelExpansions.TestData.DataModels
{ {
public PluginSubDataModel() public PluginSubDataModel()
{ {
ListOfInts = new List<int> { 1, 2, 3, 4, 5 }; ListOfInts = new List<int> {1, 2, 3, 4, 5};
} }
// You don't need to annotate properties, they will still show up // You don't need to annotate properties, they will still show up

View File

@ -2,8 +2,8 @@
namespace Artemis.Storage.Entities.Profile.Abstract namespace Artemis.Storage.Entities.Profile.Abstract
{ {
public abstract class DisplayConditionPartEntity public abstract class DataModelConditionPartEntity
{ {
public List<DisplayConditionPartEntity> Children { get; set; } public List<DataModelConditionPartEntity> Children { get; set; }
} }
} }

View File

@ -16,6 +16,6 @@ namespace Artemis.Storage.Entities.Profile.Abstract
public List<PropertyEntity> PropertyEntities { get; set; } public List<PropertyEntity> PropertyEntities { get; set; }
public List<string> ExpandedPropertyGroups { get; set; } public List<string> ExpandedPropertyGroups { get; set; }
public DisplayConditionGroupEntity RootDisplayCondition { get; set; } public DataModelConditionGroupEntity DisplayCondition { get; set; }
} }
} }

View File

@ -3,11 +3,11 @@ using Artemis.Storage.Entities.Profile.Abstract;
namespace Artemis.Storage.Entities.Profile.Conditions namespace Artemis.Storage.Entities.Profile.Conditions
{ {
public class DisplayConditionGroupEntity : DisplayConditionPartEntity public class DataModelConditionGroupEntity : DataModelConditionPartEntity
{ {
public DisplayConditionGroupEntity() public DataModelConditionGroupEntity()
{ {
Children = new List<DisplayConditionPartEntity>(); Children = new List<DataModelConditionPartEntity>();
} }
public int BooleanOperator { get; set; } public int BooleanOperator { get; set; }

View File

@ -4,11 +4,11 @@ using Artemis.Storage.Entities.Profile.Abstract;
namespace Artemis.Storage.Entities.Profile.Conditions namespace Artemis.Storage.Entities.Profile.Conditions
{ {
public class DisplayConditionListEntity : DisplayConditionPartEntity public class DataModelConditionListEntity : DataModelConditionPartEntity
{ {
public DisplayConditionListEntity() public DataModelConditionListEntity()
{ {
Children = new List<DisplayConditionPartEntity>(); Children = new List<DataModelConditionPartEntity>();
} }
public Guid? ListDataModelGuid { get; set; } public Guid? ListDataModelGuid { get; set; }

View File

@ -3,7 +3,7 @@ using Artemis.Storage.Entities.Profile.Abstract;
namespace Artemis.Storage.Entities.Profile.Conditions namespace Artemis.Storage.Entities.Profile.Conditions
{ {
public class DisplayConditionListPredicateEntity : DisplayConditionPartEntity public class DataModelConditionListPredicateEntity : DataModelConditionPartEntity
{ {
public int PredicateType { get; set; } public int PredicateType { get; set; }
@ -12,6 +12,7 @@ namespace Artemis.Storage.Entities.Profile.Conditions
public string LeftPropertyPath { get; set; } public string LeftPropertyPath { get; set; }
public Guid? RightDataModelGuid { get; set; }
public string RightPropertyPath { get; set; } public string RightPropertyPath { get; set; }
// Stored as a string to be able to control serialization and deserialization ourselves // Stored as a string to be able to control serialization and deserialization ourselves

View File

@ -3,7 +3,7 @@ using Artemis.Storage.Entities.Profile.Abstract;
namespace Artemis.Storage.Entities.Profile.Conditions namespace Artemis.Storage.Entities.Profile.Conditions
{ {
public class DisplayConditionPredicateEntity : DisplayConditionPartEntity public class DataModelConditionPredicateEntity : DataModelConditionPartEntity
{ {
public int PredicateType { get; set; } public int PredicateType { get; set; }
public Guid? LeftDataModelGuid { get; set; } public Guid? LeftDataModelGuid { get; set; }

View File

@ -1,9 +0,0 @@
using System;
namespace Artemis.Storage.Entities.Profile.Conditions
{
public class ProfileConditionEntity
{
public Guid Id { get; set; }
}
}

View File

@ -5,6 +5,6 @@ namespace Artemis.Storage.Entities.Profile.DataBindings
public class DataBindingConditionValueEntity public class DataBindingConditionValueEntity
{ {
public string Value { get; set; } public string Value { get; set; }
public DisplayConditionGroupEntity RootGroup { get; set; } public DataModelConditionGroupEntity RootGroup { get; set; }
} }
} }

View File

@ -13,7 +13,7 @@
<UserControl.Resources> <UserControl.Resources>
<ResourceDictionary> <ResourceDictionary>
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Artemis.UI.Shared;component/ResourceDictionaries/DisplayConditions.xaml" /> <ResourceDictionary Source="pack://application:,,,/Artemis.UI.Shared;component/ResourceDictionaries/DataModelConditions.xaml" />
<ResourceDictionary> <ResourceDictionary>
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" /> <shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<shared:BindingProxy x:Key="DataContextProxy" Data="{Binding}" /> <shared:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
@ -32,7 +32,7 @@
<Button Background="{Binding ButtonBrush}" <Button Background="{Binding ButtonBrush}"
BorderBrush="{Binding ButtonBrush}" BorderBrush="{Binding ButtonBrush}"
Style="{StaticResource DisplayConditionButton}" Style="{StaticResource DataModelConditionButton}"
ToolTip="{Binding SelectedPropertyViewModel.DisplayPropertyPath}" ToolTip="{Binding SelectedPropertyViewModel.DisplayPropertyPath}"
IsEnabled="{Binding IsEnabled}" IsEnabled="{Binding IsEnabled}"
HorizontalAlignment="Left" HorizontalAlignment="Left"

View File

@ -75,7 +75,7 @@ namespace Artemis.UI.Shared.Input
public DataModelVisualizationViewModel SelectedPropertyViewModel public DataModelVisualizationViewModel SelectedPropertyViewModel
{ {
get => _selectedPropertyViewModel; get => _selectedPropertyViewModel;
private set => SetAndNotify(ref _selectedPropertyViewModel, value); set => SetAndNotify(ref _selectedPropertyViewModel, value);
} }
public void PopulateSelectedPropertyViewModel(DataModel datamodel, string path) public void PopulateSelectedPropertyViewModel(DataModel datamodel, string path)
@ -86,6 +86,17 @@ namespace Artemis.UI.Shared.Input
SelectedPropertyViewModel = DataModelViewModel.GetChildByPath(datamodel.PluginInfo.Guid, path); SelectedPropertyViewModel = DataModelViewModel.GetChildByPath(datamodel.PluginInfo.Guid, path);
} }
public void ChangeDataModel(DataModelPropertiesViewModel dataModel)
{
if (DataModelViewModel != null)
DataModelViewModel.UpdateRequested -= DataModelOnUpdateRequested;
DataModelViewModel = dataModel;
if (DataModelViewModel != null)
DataModelViewModel.UpdateRequested += DataModelOnUpdateRequested;
}
private void Initialize() private void Initialize()
{ {
// Get the data models // Get the data models

View File

@ -13,7 +13,7 @@
<UserControl.Resources> <UserControl.Resources>
<ResourceDictionary> <ResourceDictionary>
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Artemis.UI.Shared;component/ResourceDictionaries/DisplayConditions.xaml" /> <ResourceDictionary Source="pack://application:,,,/Artemis.UI.Shared;component/ResourceDictionaries/DataModelConditions.xaml" />
<ResourceDictionary> <ResourceDictionary>
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" /> <shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
</ResourceDictionary> </ResourceDictionary>
@ -22,12 +22,13 @@
</UserControl.Resources> </UserControl.Resources>
<materialDesign:Transitioner SelectedIndex="{Binding TransitionIndex}" DefaultTransitionOrigin="0.5, 0.5" Margin="3 -4"> <materialDesign:Transitioner SelectedIndex="{Binding TransitionIndex}" DefaultTransitionOrigin="0.5, 0.5" Margin="3 -4">
<Button Style="{StaticResource DisplayConditionButton}" <Button Style="{StaticResource DataModelConditionButton}"
Background="{Binding ButtonBrush}" Background="{Binding ButtonBrush}"
BorderBrush="{Binding ButtonBrush}" BorderBrush="{Binding ButtonBrush}"
Margin="0 4" Margin="0 4"
Command="{s:Action ActivateInputViewModel}" Command="{s:Action ActivateInputViewModel}"
HorizontalAlignment="Left"> HorizontalAlignment="Left"
IsEnabled="{Binding IsEnabled}">
<Grid> <Grid>
<StackPanel Visibility="{Binding Value, Converter={StaticResource NullToVisibilityConverter}}" Orientation="Horizontal"> <StackPanel Visibility="{Binding Value, Converter={StaticResource NullToVisibilityConverter}}" Orientation="Horizontal">
<TextBlock FontWeight="Light" <TextBlock FontWeight="Light"

View File

@ -22,15 +22,17 @@ namespace Artemis.UI.Shared.Input
private Type _targetType; private Type _targetType;
private int _transitionIndex; private int _transitionIndex;
private object _value; private object _value;
private bool _isEnabled;
internal DataModelStaticViewModel(Type targetType, IDataModelUIService dataModelUIService) internal DataModelStaticViewModel(Type targetType, IDataModelUIService dataModelUIService)
{ {
TargetType = targetType;
_dataModelUIService = dataModelUIService; _dataModelUIService = dataModelUIService;
DisplayViewModel = _dataModelUIService.GetDataModelDisplayViewModel(TargetType, true);
_rootView = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive); _rootView = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
TargetType = targetType;
IsEnabled = TargetType != null;
DisplayViewModel = _dataModelUIService.GetDataModelDisplayViewModel(TargetType ?? typeof(object), true);
if (_rootView != null) if (_rootView != null)
{ {
_rootView.MouseUp += RootViewOnMouseUp; _rootView.MouseUp += RootViewOnMouseUp;
@ -90,6 +92,12 @@ namespace Artemis.UI.Shared.Input
set => SetAndNotify(ref _placeholder, value); set => SetAndNotify(ref _placeholder, value);
} }
public bool IsEnabled
{
get => _isEnabled;
private set => SetAndNotify(ref _isEnabled, value);
}
#region IDisposable #region IDisposable
public void Dispose() public void Dispose()
@ -116,14 +124,24 @@ namespace Artemis.UI.Shared.Input
public void UpdateTargetType(Type target) public void UpdateTargetType(Type target)
{ {
TargetType = target ?? throw new ArgumentNullException(nameof(target)); TargetType = target;
DisplayViewModel = _dataModelUIService.GetDataModelDisplayViewModel(TargetType ?? typeof(object), true);
IsEnabled = TargetType != null;
// If null, clear the input
if (TargetType == null)
{
ApplyFreeInput(null, true);
return;
}
// If the type changed, reset to the default type // If the type changed, reset to the default type
if (!target.IsCastableFrom(Value.GetType())) if (Value == null || !target.IsCastableFrom(Value.GetType()))
{ {
// Force the VM to close if it was open and apply the new value // Force the VM to close if it was open and apply the new value
ApplyFreeInput(target.GetDefault(), true); ApplyFreeInput(target.GetDefault(), true);
} }
} }
private void ApplyFreeInput(object value, bool submitted) private void ApplyFreeInput(object value, bool submitted)

View File

@ -36,7 +36,7 @@ namespace Artemis.UI.Shared
public DataModelPropertiesViewModel GetListTypeViewModel(IDataModelUIService dataModelUIService) public DataModelPropertiesViewModel GetListTypeViewModel(IDataModelUIService dataModelUIService)
{ {
// Create a property VM describing the type of the list // Create a property VM describing the type of the list
var viewModel = CreateListChild(dataModelUIService, List.GetType().GenericTypeArguments[0]); var viewModel = CreateListChild(dataModelUIService, PropertyInfo.PropertyType.GenericTypeArguments[0]);
// 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)

View File

@ -176,9 +176,9 @@ namespace Artemis.UI.Shared
IsMatchingFilteredTypes = filteredTypes.Any(t => t == PropertyInfo.PropertyType || t == typeof(Enum) && PropertyInfo.PropertyType.IsEnum); IsMatchingFilteredTypes = filteredTypes.Any(t => t == PropertyInfo.PropertyType || t == typeof(Enum) && PropertyInfo.PropertyType.IsEnum);
} }
public DataModelVisualizationViewModel GetChildForCondition(DataModelConditionPredicate predicate, DisplayConditionSide side) public DataModelVisualizationViewModel GetChildForCondition(DataModelConditionPredicate predicate, DataModelConditionSide side)
{ {
if (side == DisplayConditionSide.Left) if (side == DataModelConditionSide.Left)
{ {
if (predicate.LeftDataModel == null || predicate.LeftPropertyPath == null) if (predicate.LeftDataModel == null || predicate.LeftPropertyPath == null)
return null; return null;
@ -289,7 +289,7 @@ namespace Artemis.UI.Shared
#endregion #endregion
} }
public enum DisplayConditionSide public enum DataModelConditionSide
{ {
Left, Left,
Right Right

View File

@ -11,7 +11,6 @@
<Grid> <Grid>
<!-- Value display --> <!-- Value display -->
<TextBlock Text="{Binding DisplayValue, Mode=OneWay}" <TextBlock Text="{Binding DisplayValue, Mode=OneWay}"
FontFamily="Consolas"
HorizontalAlignment="Right" HorizontalAlignment="Right"
Visibility="{Binding ShowToString, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}" /> Visibility="{Binding ShowToString, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}" />
<TextBlock Text="null" <TextBlock Text="null"

View File

@ -3,7 +3,7 @@
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
xmlns:dataModel="clr-namespace:Artemis.UI.Shared"> xmlns:dataModel="clr-namespace:Artemis.UI.Shared">
<Style x:Key="DisplayConditionButton" TargetType="{x:Type Button}" BasedOn="{StaticResource MaterialDesignFlatAccentBgButton}"> <Style x:Key="DataModelConditionButton" TargetType="{x:Type Button}" BasedOn="{StaticResource MaterialDesignFlatAccentBgButton}">
<Setter Property="Margin" Value="3 0" /> <Setter Property="Margin" Value="3 0" />
<Setter Property="Padding" Value="6 4" /> <Setter Property="Padding" Value="6 4" />
<Setter Property="Height" Value="22" /> <Setter Property="Height" Value="22" />
@ -11,7 +11,7 @@
<Setter Property="FontSize" Value="12" /> <Setter Property="FontSize" Value="12" />
</Style> </Style>
<Style x:Key="DisplayConditionButtonLeftClickMenu" TargetType="{x:Type Button}" BasedOn="{StaticResource DisplayConditionButton}"> <Style x:Key="DataModelConditionButtonLeftClickMenu" TargetType="{x:Type Button}" BasedOn="{StaticResource DataModelConditionButton}">
<Style.Triggers> <Style.Triggers>
<EventTrigger RoutedEvent="Click"> <EventTrigger RoutedEvent="Click">
<EventTrigger.Actions> <EventTrigger.Actions>

View File

@ -201,7 +201,6 @@ namespace Artemis.UI.Shared.Services
public DataModelStaticViewModel GetStaticInputViewModel(Type targetType) public DataModelStaticViewModel GetStaticInputViewModel(Type targetType)
{ {
if (targetType == null) throw new ArgumentNullException(nameof(targetType));
return _kernel.Get<DataModelStaticViewModel>(new ConstructorArgument("targetType", targetType)); return _kernel.Get<DataModelStaticViewModel>(new ConstructorArgument("targetType", targetType));
} }

View File

@ -215,10 +215,10 @@
<Compile Remove="obj\x64\Debug\Screens\Module\ProfileEditor\Dialogs\ProfileCreateView.g.i.cs" /> <Compile Remove="obj\x64\Debug\Screens\Module\ProfileEditor\Dialogs\ProfileCreateView.g.i.cs" />
<Compile Remove="obj\x64\Debug\Screens\Module\ProfileEditor\Dialogs\ProfileElementRenameView.g.cs" /> <Compile Remove="obj\x64\Debug\Screens\Module\ProfileEditor\Dialogs\ProfileElementRenameView.g.cs" />
<Compile Remove="obj\x64\Debug\Screens\Module\ProfileEditor\Dialogs\ProfileElementRenameView.g.i.cs" /> <Compile Remove="obj\x64\Debug\Screens\Module\ProfileEditor\Dialogs\ProfileElementRenameView.g.i.cs" />
<Compile Remove="obj\x64\Debug\Screens\Module\ProfileEditor\DisplayConditions\DisplayConditionsView.g.cs" /> <Compile Remove="obj\x64\Debug\Screens\Module\ProfileEditor\DataModelConditions\DataModelConditionsView.g.cs" />
<Compile Remove="obj\x64\Debug\Screens\Module\ProfileEditor\DisplayConditions\DisplayConditionsView.g.i.cs" /> <Compile Remove="obj\x64\Debug\Screens\Module\ProfileEditor\DataModelConditions\DataModelConditionsView.g.i.cs" />
<Compile Remove="obj\x64\Debug\Screens\Module\ProfileEditor\DisplayConditions\DisplayConditionView.g.cs" /> <Compile Remove="obj\x64\Debug\Screens\Module\ProfileEditor\DataModelConditions\DataModelConditionView.g.cs" />
<Compile Remove="obj\x64\Debug\Screens\Module\ProfileEditor\DisplayConditions\DisplayConditionView.g.i.cs" /> <Compile Remove="obj\x64\Debug\Screens\Module\ProfileEditor\DataModelConditions\DataModelConditionView.g.i.cs" />
<Compile Remove="obj\x64\Debug\Screens\Module\ProfileEditor\ElementProperties\ElementPropertiesView.g.i.cs" /> <Compile Remove="obj\x64\Debug\Screens\Module\ProfileEditor\ElementProperties\ElementPropertiesView.g.i.cs" />
<Compile Remove="obj\x64\Debug\Screens\Module\ProfileEditor\ElementProperties\ElementPropertyView.g.cs" /> <Compile Remove="obj\x64\Debug\Screens\Module\ProfileEditor\ElementProperties\ElementPropertyView.g.cs" />
<Compile Remove="obj\x64\Debug\Screens\Module\ProfileEditor\LayerElements\LayerElementsView.g.i.cs" /> <Compile Remove="obj\x64\Debug\Screens\Module\ProfileEditor\LayerElements\LayerElementsView.g.i.cs" />
@ -239,8 +239,8 @@
<Compile Remove="obj\x64\Debug\Screens\Settings\Tabs\Devices\DeviceSettingsView.g.cs" /> <Compile Remove="obj\x64\Debug\Screens\Settings\Tabs\Devices\DeviceSettingsView.g.cs" />
<Compile Remove="obj\x64\Debug\Screens\Settings\Tabs\Devices\DeviceSettingsView.g.i.cs" /> <Compile Remove="obj\x64\Debug\Screens\Settings\Tabs\Devices\DeviceSettingsView.g.i.cs" />
<Compile Remove="obj\x64\Release\Screens\Module\ProfileEditor\Dialogs\ProfileCreateView.g.cs" /> <Compile Remove="obj\x64\Release\Screens\Module\ProfileEditor\Dialogs\ProfileCreateView.g.cs" />
<Compile Remove="obj\x64\Release\Screens\Module\ProfileEditor\DisplayConditions\DisplayConditionsView.g.cs" /> <Compile Remove="obj\x64\Release\Screens\Module\ProfileEditor\DataModelConditions\DataModelConditionsView.g.cs" />
<Compile Remove="obj\x64\Release\Screens\Module\ProfileEditor\DisplayConditions\DisplayConditionView.g.cs" /> <Compile Remove="obj\x64\Release\Screens\Module\ProfileEditor\DataModelConditions\DataModelConditionView.g.cs" />
<Compile Remove="obj\x64\Release\Screens\Module\ProfileEditor\ElementProperties\ElementPropertiesView.g.cs" /> <Compile Remove="obj\x64\Release\Screens\Module\ProfileEditor\ElementProperties\ElementPropertiesView.g.cs" />
<Compile Remove="obj\x64\Release\Screens\Module\ProfileEditor\ElementProperties\ElementPropertyView.g.cs" /> <Compile Remove="obj\x64\Release\Screens\Module\ProfileEditor\ElementProperties\ElementPropertyView.g.cs" />
<Compile Remove="obj\x64\Release\Screens\Module\ProfileEditor\LayerElements\LayerElementsView.g.cs" /> <Compile Remove="obj\x64\Release\Screens\Module\ProfileEditor\LayerElements\LayerElementsView.g.cs" />

View File

@ -3,7 +3,7 @@ using Artemis.Core.Modules;
using Artemis.UI.Screens.Modules; using Artemis.UI.Screens.Modules;
using Artemis.UI.Screens.Modules.Tabs; using Artemis.UI.Screens.Modules.Tabs;
using Artemis.UI.Screens.ProfileEditor; using Artemis.UI.Screens.ProfileEditor;
using Artemis.UI.Screens.ProfileEditor.DisplayConditions; using Artemis.UI.Screens.ProfileEditor.Conditions;
using Artemis.UI.Screens.ProfileEditor.LayerProperties; using Artemis.UI.Screens.ProfileEditor.LayerProperties;
using Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings; using Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings;
using Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.ConditionalDataBinding; using Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.ConditionalDataBinding;
@ -63,12 +63,12 @@ namespace Artemis.UI.Ninject.Factories
SelectionRemoveToolViewModel SelectionRemoveToolViewModel(ProfileViewModel profileViewModel); SelectionRemoveToolViewModel SelectionRemoveToolViewModel(ProfileViewModel profileViewModel);
} }
public interface IDisplayConditionsVmFactory : IVmFactory public interface IDataModelConditionsVmFactory : IVmFactory
{ {
DisplayConditionGroupViewModel DisplayConditionGroupViewModel(DataModelConditionGroup dataModelConditionGroup, bool isListGroup); DataModelConditionGroupViewModel DataModelConditionGroupViewModel(DataModelConditionGroup dataModelConditionGroup, bool isListGroup);
DisplayConditionListViewModel DisplayConditionListViewModel(DataModelConditionList dataModelConditionList); DataModelConditionListViewModel DataModelConditionListViewModel(DataModelConditionList dataModelConditionList);
DisplayConditionPredicateViewModel DisplayConditionPredicateViewModel(DataModelConditionPredicate dataModelConditionPredicate); DataModelConditionPredicateViewModel DataModelConditionPredicateViewModel(DataModelConditionPredicate dataModelConditionPredicate);
DisplayConditionListPredicateViewModel DisplayConditionListPredicateViewModel(DataModelConditionListPredicate dataModelConditionListPredicate); DataModelConditionListPredicateViewModel DataModelConditionListPredicateViewModel(DataModelConditionListPredicate dataModelConditionListPredicate);
} }
public interface ILayerPropertyVmFactory : IVmFactory public interface ILayerPropertyVmFactory : IVmFactory

View File

@ -3,7 +3,7 @@
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
xmlns:dataModel="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"> xmlns:dataModel="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared">
<Style x:Key="DisplayConditionButton" TargetType="{x:Type Button}" BasedOn="{StaticResource MaterialDesignFlatAccentBgButton}"> <Style x:Key="DataModelConditionButton" TargetType="{x:Type Button}" BasedOn="{StaticResource MaterialDesignFlatAccentBgButton}">
<Setter Property="Margin" Value="3 0" /> <Setter Property="Margin" Value="3 0" />
<Setter Property="Padding" Value="6 4" /> <Setter Property="Padding" Value="6 4" />
<Setter Property="Height" Value="22" /> <Setter Property="Height" Value="22" />
@ -11,7 +11,7 @@
<Setter Property="FontSize" Value="12" /> <Setter Property="FontSize" Value="12" />
</Style> </Style>
<Style x:Key="DisplayConditionButtonLeftClickMenu" TargetType="{x:Type Button}" BasedOn="{StaticResource DisplayConditionButton}"> <Style x:Key="DataModelConditionButtonLeftClickMenu" TargetType="{x:Type Button}" BasedOn="{StaticResource DataModelConditionButton}">
<Style.Triggers> <Style.Triggers>
<EventTrigger RoutedEvent="Click"> <EventTrigger RoutedEvent="Click">
<EventTrigger.Actions> <EventTrigger.Actions>

View File

@ -0,0 +1,23 @@
using Artemis.Core;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.Conditions.Abstract
{
public abstract class DataModelConditionViewModel : Conductor<DataModelConditionViewModel>.Collection.AllActive
{
protected DataModelConditionViewModel(DataModelConditionPart model)
{
Model = model;
}
public DataModelConditionPart Model { get; }
public abstract void Update();
public virtual void Delete()
{
Model.Parent.RemoveChild(Model);
((DataModelConditionViewModel) Parent).Update();
}
}
}

View File

@ -3,19 +3,19 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.DisplayConditions"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
xmlns:Converters="clr-namespace:Artemis.UI.Converters" xmlns:Converters="clr-namespace:Artemis.UI.Converters"
xmlns:utilities="clr-namespace:Artemis.UI.Utilities" xmlns:utilities="clr-namespace:Artemis.UI.Utilities"
x:Class="Artemis.UI.Screens.ProfileEditor.DisplayConditions.DisplayConditionGroupView" xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.Conditions"
x:Class="Artemis.UI.Screens.ProfileEditor.Conditions.DataModelConditionGroupView"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance IsDesignTimeCreatable=False, Type={x:Type local:DisplayConditionGroupViewModel}}"> d:DataContext="{d:DesignInstance IsDesignTimeCreatable=False, Type={x:Type local:DataModelConditionGroupViewModel}}">
<UserControl.Resources> <UserControl.Resources>
<ResourceDictionary> <ResourceDictionary>
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DisplayConditions.xaml" /> <ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DataModelConditions.xaml" />
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
<utilities:BindingProxy x:Key="DataContextProxy" Data="{Binding}" /> <utilities:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
</ResourceDictionary> </ResourceDictionary>
@ -46,7 +46,7 @@
<Button Grid.Row="0" <Button Grid.Row="0"
Grid.Column="1" Grid.Column="1"
ToolTip="Change the operator of the group" ToolTip="Change the operator of the group"
Style="{StaticResource DisplayConditionButtonLeftClickMenu}" Style="{StaticResource DataModelConditionButtonLeftClickMenu}"
Background="#E74C4C" Background="#E74C4C"
BorderBrush="#E74C4C" BorderBrush="#E74C4C"
Margin="3 1" Margin="3 1"

View File

@ -3,29 +3,30 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Ninject.Factories; using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract; using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
using Artemis.UI.Screens.ProfileEditor.DisplayConditions;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Humanizer; using Humanizer;
using Stylet; using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions namespace Artemis.UI.Screens.ProfileEditor.Conditions
{ {
public class DisplayConditionGroupViewModel : DisplayConditionViewModel public class DataModelConditionGroupViewModel : DataModelConditionViewModel
{ {
private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory; private readonly IDataModelConditionsVmFactory _dataModelConditionsVmFactory;
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private bool _isInitialized; private bool _isInitialized;
private bool _isRootGroup; private bool _isRootGroup;
public DisplayConditionGroupViewModel(DataModelConditionGroup dataModelConditionGroup, public DataModelConditionGroupViewModel(DataModelConditionGroup dataModelConditionGroup,
bool isListGroup, bool isListGroup,
IProfileEditorService profileEditorService, IProfileEditorService profileEditorService,
IDisplayConditionsVmFactory displayConditionsVmFactory) IDataModelConditionsVmFactory dataModelConditionsVmFactory)
: base(dataModelConditionGroup) : base(dataModelConditionGroup)
{ {
IsListGroup = isListGroup; IsListGroup = isListGroup;
_profileEditorService = profileEditorService; _profileEditorService = profileEditorService;
_displayConditionsVmFactory = displayConditionsVmFactory; _dataModelConditionsVmFactory = dataModelConditionsVmFactory;
Items.CollectionChanged += (sender, args) => NotifyOfPropertyChange(nameof(DisplayBooleanOperator)); Items.CollectionChanged += (sender, args) => NotifyOfPropertyChange(nameof(DisplayBooleanOperator));
@ -71,15 +72,17 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
if (!IsListGroup) if (!IsListGroup)
DataModelConditionGroup.AddChild(new DataModelConditionPredicate(DataModelConditionGroup, ProfileRightSideType.Static)); DataModelConditionGroup.AddChild(new DataModelConditionPredicate(DataModelConditionGroup, ProfileRightSideType.Static));
else else
DataModelConditionGroup.AddChild(new DataModelConditionListPredicate(DataModelConditionGroup, ProfileRightSideType.Static)); DataModelConditionGroup.AddChild(new DataModelConditionListPredicate(DataModelConditionGroup, ListRightSideType.Static));
} }
else if (type == "Dynamic") else if (type == "Dynamic")
{ {
if (!IsListGroup) if (!IsListGroup)
DataModelConditionGroup.AddChild(new DataModelConditionPredicate(DataModelConditionGroup, ProfileRightSideType.Dynamic)); DataModelConditionGroup.AddChild(new DataModelConditionPredicate(DataModelConditionGroup, ProfileRightSideType.Dynamic));
else else
DataModelConditionGroup.AddChild(new DataModelConditionListPredicate(DataModelConditionGroup, ProfileRightSideType.Dynamic)); DataModelConditionGroup.AddChild(new DataModelConditionListPredicate(DataModelConditionGroup, ListRightSideType.Dynamic));
} }
else if (type == "DynamicExternal" && IsListGroup)
DataModelConditionGroup.AddChild(new DataModelConditionListPredicate(DataModelConditionGroup, ListRightSideType.DynamicExternal));
else if (type == "List" && !IsListGroup) else if (type == "List" && !IsListGroup)
DataModelConditionGroup.AddChild(new DataModelConditionList(DataModelConditionGroup)); DataModelConditionGroup.AddChild(new DataModelConditionList(DataModelConditionGroup));
@ -102,8 +105,8 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
// Remove VMs of effects no longer applied on the layer // Remove VMs of effects no longer applied on the layer
var toRemove = Items.Where(c => !DataModelConditionGroup.Children.Contains(c.Model)).ToList(); var toRemove = Items.Where(c => !DataModelConditionGroup.Children.Contains(c.Model)).ToList();
// Using RemoveRange breaks our lovely animations // Using RemoveRange breaks our lovely animations
foreach (var displayConditionViewModel in toRemove) foreach (var DataModelConditionViewModel in toRemove)
Items.Remove(displayConditionViewModel); Items.Remove(DataModelConditionViewModel);
foreach (var childModel in Model.Children) foreach (var childModel in Model.Children)
{ {
@ -112,19 +115,19 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
switch (childModel) switch (childModel)
{ {
case DataModelConditionGroup displayConditionGroup: case DataModelConditionGroup DataModelConditionGroup:
Items.Add(_displayConditionsVmFactory.DisplayConditionGroupViewModel(displayConditionGroup, IsListGroup)); Items.Add(_dataModelConditionsVmFactory.DataModelConditionGroupViewModel(DataModelConditionGroup, IsListGroup));
break; break;
case DataModelConditionList displayConditionListPredicate: case DataModelConditionList DataModelConditionListPredicate:
Items.Add(_displayConditionsVmFactory.DisplayConditionListViewModel(displayConditionListPredicate)); Items.Add(_dataModelConditionsVmFactory.DataModelConditionListViewModel(DataModelConditionListPredicate));
break; break;
case DataModelConditionPredicate displayConditionPredicate: case DataModelConditionPredicate DataModelConditionPredicate:
if (!IsListGroup) if (!IsListGroup)
Items.Add(_displayConditionsVmFactory.DisplayConditionPredicateViewModel(displayConditionPredicate)); Items.Add(_dataModelConditionsVmFactory.DataModelConditionPredicateViewModel(DataModelConditionPredicate));
break; break;
case DataModelConditionListPredicate displayConditionListPredicate: case DataModelConditionListPredicate DataModelConditionListPredicate:
if (IsListGroup) if (IsListGroup)
Items.Add(_displayConditionsVmFactory.DisplayConditionListPredicateViewModel(displayConditionListPredicate)); Items.Add(_dataModelConditionsVmFactory.DataModelConditionListPredicateViewModel(DataModelConditionListPredicate));
break; break;
} }
} }
@ -132,8 +135,8 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
foreach (var childViewModel in Items) foreach (var childViewModel in Items)
childViewModel.Update(); childViewModel.Update();
if (IsRootGroup) if (IsRootGroup && Parent is DisplayConditionsViewModel displayConditionsViewModel)
((DisplayConditionsViewModel) Parent).DisplayStartHint = !Items.Any(); displayConditionsViewModel.DisplayStartHint = !Items.Any();
} }
} }
} }

View File

@ -0,0 +1,104 @@
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:utilities="clr-namespace:Artemis.UI.Utilities"
xmlns:DataModelConditions="clr-namespace:Artemis.UI.Screens.ProfileEditor.Conditions"
x:Class="Artemis.UI.Screens.ProfileEditor.Conditions.DataModelConditionListPredicateView"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance IsDesignTimeCreatable=False, Type={x:Type DataModelConditions:DataModelConditionListPredicateViewModel}}">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DataModelConditions.xaml" />
<ResourceDictionary>
<converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<utilities:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
<DataTemplate x:Key="DataModelDataTemplate">
<Control x:Name="TemplateControl" Focusable="False" Template="{StaticResource DataModelSelectionTemplate}" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Data.ShowDataModelValues.Value, Source={StaticResource DataContextProxy}}" Value="True">
<Setter TargetName="TemplateControl" Property="Template" Value="{StaticResource DataModelSelectionTemplateWithValues}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Margin="0 3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Grid.Row="0"
Grid.Column="0"
ToolTip="Delete the predicate"
Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Left"
Foreground="#E74C4C"
Width="25"
Height="25"
Command="{s:Action Delete}">
<materialDesign:PackIcon Kind="Close" Width="18" Height="18" />
</Button>
<!-- Left side, may be null for primitive lists -->
<ContentControl Grid.Row="0"
Grid.Column="1"
s:View.Model="{Binding LeftSideSelectionViewModel}"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsTabStop="False" />
<!-- Operator -->
<Button Grid.Row="0"
Grid.Column="2"
Style="{StaticResource DataModelConditionButtonLeftClickMenu}"
Background="#7B7B7B"
BorderBrush="#7B7B7B"
Content="{Binding SelectedOperator.Description}"
Click="PropertyButton_OnClick">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding Operators}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="{Binding Icon}" VerticalAlignment="Center" Margin="0 0 15 0" />
<TextBlock Text="{Binding Description}" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
</ContextMenu.ItemTemplate>
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MaterialDesignMenuItem}">
<Setter Property="Command" Value="{Binding Data.SelectOperatorCommand, Source={StaticResource DataContextProxy}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
<Setter Property="CommandTarget" Value="{Binding}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
</Button>
<!-- Right side, either a selection or an input -->
<ContentControl Grid.Row="0"
Grid.Column="3"
s:View.Model="{Binding RightSideSelectionViewModel}"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsTabStop="False" />
<ContentControl Grid.Row="0"
Grid.Column="3"
s:View.Model="{Binding RightSideInputViewModel}"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsTabStop="False" />
</Grid>
</UserControl>

View File

@ -1,14 +1,14 @@
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions namespace Artemis.UI.Screens.ProfileEditor.Conditions
{ {
/// <summary> /// <summary>
/// Interaction logic for DisplayConditionListPredicateView.xaml /// Interaction logic for DataModelConditionListPredicateView.xaml
/// </summary> /// </summary>
public partial class DisplayConditionListPredicateView : UserControl public partial class DataModelConditionListPredicateView : UserControl
{ {
public DisplayConditionListPredicateView() public DataModelConditionListPredicateView()
{ {
InitializeComponent(); InitializeComponent();
} }

View File

@ -0,0 +1,285 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Exceptions;
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Input;
using Artemis.UI.Shared.Services;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.Conditions
{
public class DataModelConditionListPredicateViewModel : DataModelConditionViewModel, IDisposable
{
private readonly IConditionOperatorService _conditionOperatorService;
private readonly IDataModelUIService _dataModelUIService;
private readonly IEventAggregator _eventAggregator;
private readonly IProfileEditorService _profileEditorService;
private bool _isPrimitiveList;
private DataModelDynamicViewModel _leftSideSelectionViewModel;
private BindableCollection<ConditionOperator> _operators;
private DataModelStaticViewModel _rightSideInputViewModel;
private DataModelDynamicViewModel _rightSideSelectionViewModel;
private ConditionOperator _selectedOperator;
private List<Type> _supportedInputTypes;
public DataModelConditionListPredicateViewModel(
DataModelConditionListPredicate dataModelConditionListPredicate,
IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService,
IConditionOperatorService conditionOperatorService,
IEventAggregator eventAggregator) : base(dataModelConditionListPredicate)
{
_profileEditorService = profileEditorService;
_dataModelUIService = dataModelUIService;
_conditionOperatorService = conditionOperatorService;
_eventAggregator = eventAggregator;
_supportedInputTypes = new List<Type>();
SelectOperatorCommand = new DelegateCommand(ExecuteSelectOperatorCommand);
Operators = new BindableCollection<ConditionOperator>();
Initialize();
}
public DataModelConditionListPredicate DataModelConditionListPredicate => (DataModelConditionListPredicate) Model;
public BindableCollection<ConditionOperator> Operators
{
get => _operators;
set => SetAndNotify(ref _operators, value);
}
public DataModelDynamicViewModel LeftSideSelectionViewModel
{
get => _leftSideSelectionViewModel;
set => SetAndNotify(ref _leftSideSelectionViewModel, value);
}
public DataModelDynamicViewModel RightSideSelectionViewModel
{
get => _rightSideSelectionViewModel;
set => SetAndNotify(ref _rightSideSelectionViewModel, value);
}
public DataModelStaticViewModel RightSideInputViewModel
{
get => _rightSideInputViewModel;
set => SetAndNotify(ref _rightSideInputViewModel, value);
}
public ConditionOperator SelectedOperator
{
get => _selectedOperator;
set => SetAndNotify(ref _selectedOperator, value);
}
public DelegateCommand SelectOperatorCommand { get; }
public void Dispose()
{
if (!_isPrimitiveList)
{
LeftSideSelectionViewModel.PropertySelected -= LeftSideOnPropertySelected;
LeftSideSelectionViewModel.Dispose();
}
DisposeRightSideDynamic();
DisposeRightSideStatic();
}
public override void Delete()
{
base.Delete();
_profileEditorService.UpdateSelectedProfileElement();
}
public void Initialize()
{
var listDataModel = GetListDataModel();
if (listDataModel.Children.Count == 1 && listDataModel.Children.First() is DataModelListPropertyViewModel)
_isPrimitiveList = true;
else
_isPrimitiveList = false;
// Get the data models
if (!_isPrimitiveList)
{
LeftSideSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule());
LeftSideSelectionViewModel.ChangeDataModel((DataModelPropertiesViewModel) listDataModel);
LeftSideSelectionViewModel.PropertySelected += LeftSideOnPropertySelected;
}
// Determine which types are currently supported
var editors = _dataModelUIService.RegisteredDataModelEditors;
_supportedInputTypes = editors.Select(e => e.SupportedType).ToList();
_supportedInputTypes.AddRange(editors.Where(e => e.CompatibleConversionTypes != null).SelectMany(e => e.CompatibleConversionTypes));
Update();
}
public override void Update()
{
var listDataModelGuid = DataModelConditionListPredicate.ListDataModel.PluginInfo.Guid;
if (!_isPrimitiveList)
{
LeftSideSelectionViewModel.FilterTypes = _supportedInputTypes.ToArray();
LeftSideSelectionViewModel.ButtonBrush = new SolidColorBrush(Color.FromRgb(71, 108, 188));
LeftSideSelectionViewModel.SelectedPropertyViewModel = LeftSideSelectionViewModel.DataModelViewModel.GetChildByPath(
listDataModelGuid, DataModelConditionListPredicate.LeftPropertyPath
);
}
var leftSideType = _isPrimitiveList
? DataModelConditionListPredicate.ListType
: LeftSideSelectionViewModel.SelectedPropertyViewModel?.PropertyInfo?.PropertyType;
// Get the supported operators
Operators.Clear();
Operators.AddRange(_conditionOperatorService.GetConditionOperatorsForType(leftSideType));
if (DataModelConditionListPredicate.Operator == null)
DataModelConditionListPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType)));
SelectedOperator = DataModelConditionListPredicate.Operator;
// Ensure the right side has the proper VM
if (DataModelConditionListPredicate.PredicateType == ListRightSideType.Dynamic || DataModelConditionListPredicate.PredicateType == ListRightSideType.DynamicExternal)
{
DisposeRightSideStatic();
if (RightSideSelectionViewModel == null)
{
RightSideSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule());
RightSideSelectionViewModel.ButtonBrush = (Brush) Application.Current.FindResource("PrimaryHueMidBrush");
RightSideSelectionViewModel.PropertySelected += RightSideOnPropertySelected;
}
RightSideSelectionViewModel.FilterTypes = new[] {leftSideType};
if (DataModelConditionListPredicate.PredicateType == ListRightSideType.Dynamic)
{
RightSideSelectionViewModel.SelectedPropertyViewModel = RightSideSelectionViewModel.DataModelViewModel.GetChildByPath(
listDataModelGuid, DataModelConditionListPredicate.RightPropertyPath
);
}
else
RightSideSelectionViewModel.PopulateSelectedPropertyViewModel(DataModelConditionListPredicate.RightDataModel, DataModelConditionListPredicate.RightPropertyPath);
}
else
{
DisposeRightSideDynamic();
if (RightSideInputViewModel == null)
{
RightSideInputViewModel = _dataModelUIService.GetStaticInputViewModel(leftSideType);
RightSideInputViewModel.Value = DataModelConditionListPredicate.RightStaticValue;
RightSideInputViewModel.ButtonBrush = (Brush) Application.Current.FindResource("PrimaryHueMidBrush");
RightSideInputViewModel.ValueUpdated += RightSideOnValueEntered;
}
if (RightSideInputViewModel.TargetType != leftSideType)
RightSideInputViewModel.UpdateTargetType(leftSideType);
}
}
public void ApplyLeftSide()
{
DataModelConditionListPredicate.UpdateLeftSide(LeftSideSelectionViewModel.SelectedPropertyViewModel.PropertyPath);
_profileEditorService.UpdateSelectedProfileElement();
SelectedOperator = DataModelConditionListPredicate.Operator;
Update();
}
public void ApplyRightSideDynamic()
{
if (DataModelConditionListPredicate.ListContainsInnerPath(RightSideSelectionViewModel.SelectedPropertyViewModel.PropertyPath))
DataModelConditionListPredicate.UpdateRightSideDynamic(RightSideSelectionViewModel.SelectedPropertyViewModel.PropertyPath);
else
DataModelConditionListPredicate.UpdateRightSideDynamic(
RightSideSelectionViewModel.SelectedPropertyViewModel.DataModel,
RightSideSelectionViewModel.SelectedPropertyViewModel.PropertyPath
);
_profileEditorService.UpdateSelectedProfileElement();
Update();
}
public void ApplyRightSideStatic(object value)
{
DataModelConditionListPredicate.UpdateRightSideStatic(value);
_profileEditorService.UpdateSelectedProfileElement();
Update();
}
public void ApplyOperator()
{
DataModelConditionListPredicate.UpdateOperator(SelectedOperator);
_profileEditorService.UpdateSelectedProfileElement();
Update();
}
private DataModelVisualizationViewModel GetListDataModel()
{
if (DataModelConditionListPredicate.ListDataModel == null || DataModelConditionListPredicate.ListPropertyPath == null)
throw new ArtemisUIException("Cannot create a list predicate without first selecting a target list");
var dataModel = _dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule(), true);
var listDataModel = (DataModelListViewModel) dataModel.GetChildByPath(
DataModelConditionListPredicate.ListDataModel.PluginInfo.Guid,
DataModelConditionListPredicate.ListPropertyPath
);
return listDataModel.GetListTypeViewModel(_dataModelUIService);
}
private void ExecuteSelectOperatorCommand(object context)
{
if (!(context is ConditionOperator dataModelConditionOperator))
return;
SelectedOperator = dataModelConditionOperator;
ApplyOperator();
}
private void LeftSideOnPropertySelected(object sender, DataModelInputDynamicEventArgs e)
{
ApplyLeftSide();
}
private void RightSideOnPropertySelected(object sender, DataModelInputDynamicEventArgs e)
{
ApplyRightSideDynamic();
}
private void RightSideOnValueEntered(object sender, DataModelInputStaticEventArgs e)
{
ApplyRightSideStatic(e.Value);
}
private void DisposeRightSideStatic()
{
if (RightSideInputViewModel != null)
{
RightSideInputViewModel.ValueUpdated -= RightSideOnValueEntered;
RightSideInputViewModel.Dispose();
RightSideInputViewModel = null;
}
}
private void DisposeRightSideDynamic()
{
if (RightSideSelectionViewModel != null)
{
RightSideSelectionViewModel.PropertySelected -= RightSideOnPropertySelected;
RightSideSelectionViewModel.Dispose();
RightSideSelectionViewModel = null;
}
}
}
}

View File

@ -0,0 +1,82 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.Conditions.DataModelConditionListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.Conditions"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance Type=local:DataModelConditionListViewModel, IsDesignTimeCreatable=False}">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DataModelConditions.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Margin="0 3">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Row="0"
Grid.Column="0"
ToolTip="Delete the list predicate and all its children"
Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Left"
Foreground="#E74C4C"
Width="25"
Height="25"
Command="{s:Action Delete}">
<materialDesign:PackIcon Kind="Close" Width="18" Height="18" />
</Button>
<!-- Left side, the list this predicate is targeting -->
<ContentControl Grid.Row="0"
Grid.Column="1"
s:View.Model="{Binding TargetSelectionViewModel}"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsTabStop="False" />
<!-- Operator -->
<Button Grid.Row="0"
Grid.Column="2"
Style="{StaticResource DataModelConditionButtonLeftClickMenu}"
Background="#7B7B7B"
BorderBrush="#7B7B7B"
Content="{Binding SelectedListOperator}"
Click="PropertyButton_OnClick"
HorizontalAlignment="Left">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Any" Command="{s:Action SelectListOperator}" CommandParameter="Any" />
<MenuItem Header="All" Command="{s:Action SelectListOperator}" CommandParameter="All" />
<MenuItem Header="None" Command="{s:Action SelectListOperator}" CommandParameter="None" />
<MenuItem Header="Count (NYI)" Command="{s:Action SelectListOperator}" CommandParameter="Count" IsEnabled="False" />
</ContextMenu>
</Button.ContextMenu>
</Button>
<ItemsControl Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" ItemsSource="{Binding Items}" Margin="0 4 0 0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<materialDesign:TransitioningContent>
<materialDesign:TransitioningContent.OpeningEffects>
<materialDesign:TransitionEffect Kind="FadeIn" />
<materialDesign:TransitionEffect Kind="SlideInFromLeft" />
</materialDesign:TransitioningContent.OpeningEffects>
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
</materialDesign:TransitioningContent>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>

View File

@ -1,14 +1,14 @@
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions namespace Artemis.UI.Screens.ProfileEditor.Conditions
{ {
/// <summary> /// <summary>
/// Interaction logic for DisplayConditionListView.xaml /// Interaction logic for DataModelConditionListView.xaml
/// </summary> /// </summary>
public partial class DisplayConditionListView : UserControl public partial class DataModelConditionListView : UserControl
{ {
public DisplayConditionListView() public DataModelConditionListView()
{ {
InitializeComponent(); InitializeComponent();
} }

View File

@ -0,0 +1,139 @@
using System;
using System.Collections;
using System.Linq;
using System.Windows.Media;
using Artemis.Core;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Input;
using Artemis.UI.Shared.Services;
using Humanizer;
namespace Artemis.UI.Screens.ProfileEditor.Conditions
{
public class DataModelConditionListViewModel : DataModelConditionViewModel, IDisposable
{
private readonly IDataModelConditionsVmFactory _dataModelConditionsVmFactory;
private readonly IDataModelUIService _dataModelUIService;
private readonly IProfileEditorService _profileEditorService;
private DataModelDynamicViewModel _targetSelectionViewModel;
public DataModelConditionListViewModel(
DataModelConditionList dataModelConditionList,
IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService,
IDataModelConditionsVmFactory dataModelConditionsVmFactory) : base(dataModelConditionList)
{
_profileEditorService = profileEditorService;
_dataModelUIService = dataModelUIService;
_dataModelConditionsVmFactory = dataModelConditionsVmFactory;
Initialize();
}
public DataModelDynamicViewModel TargetSelectionViewModel
{
get => _targetSelectionViewModel;
set => SetAndNotify(ref _targetSelectionViewModel, value);
}
public DataModelConditionList DataModelConditionList => (DataModelConditionList) Model;
public string SelectedListOperator => DataModelConditionList.ListOperator.Humanize();
public void Dispose()
{
TargetSelectionViewModel.Dispose();
TargetSelectionViewModel.PropertySelected -= TargetSelectionViewModelOnPropertySelected;
}
public void SelectListOperator(string type)
{
var enumValue = Enum.Parse<ListOperator>(type);
DataModelConditionList.ListOperator = enumValue;
NotifyOfPropertyChange(nameof(SelectedListOperator));
_profileEditorService.UpdateSelectedProfileElement();
}
public void AddCondition(string type)
{
if (type == "Static")
DataModelConditionList.AddChild(new DataModelConditionPredicate(DataModelConditionList, ProfileRightSideType.Static));
else if (type == "Dynamic")
DataModelConditionList.AddChild(new DataModelConditionPredicate(DataModelConditionList, ProfileRightSideType.Dynamic));
Update();
_profileEditorService.UpdateSelectedProfileElement();
}
public void AddGroup()
{
DataModelConditionList.AddChild(new DataModelConditionGroup(DataModelConditionList));
Update();
_profileEditorService.UpdateSelectedProfileElement();
}
public override void Delete()
{
base.Delete();
_profileEditorService.UpdateSelectedProfileElement();
}
public void Initialize()
{
TargetSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule());
TargetSelectionViewModel.FilterTypes = new[] {typeof(IList)};
TargetSelectionViewModel.ButtonBrush = new SolidColorBrush(Color.FromRgb(71, 108, 188));
TargetSelectionViewModel.Placeholder = "Select a list";
TargetSelectionViewModel.PropertySelected += TargetSelectionViewModelOnPropertySelected;
Update();
}
public void ApplyList()
{
DataModelConditionList.UpdateList(
TargetSelectionViewModel.SelectedPropertyViewModel.DataModel,
TargetSelectionViewModel.SelectedPropertyViewModel.PropertyPath
);
_profileEditorService.UpdateSelectedProfileElement();
Update();
}
public override void Update()
{
TargetSelectionViewModel.PopulateSelectedPropertyViewModel(DataModelConditionList.ListDataModel, DataModelConditionList.ListPropertyPath);
NotifyOfPropertyChange(nameof(SelectedListOperator));
// Remove VMs of effects no longer applied on the layer
var toRemove = Items.Where(c => !DataModelConditionList.Children.Contains(c.Model)).ToList();
// Using RemoveRange breaks our lovely animations
foreach (var conditionViewModel in toRemove)
Items.Remove(conditionViewModel);
foreach (var childModel in Model.Children)
{
if (Items.Any(c => c.Model == childModel))
continue;
if (!(childModel is DataModelConditionGroup dataModelConditionGroup))
continue;
var viewModel = _dataModelConditionsVmFactory.DataModelConditionGroupViewModel(dataModelConditionGroup, true);
viewModel.IsRootGroup = true;
Items.Add(viewModel);
}
foreach (var childViewModel in Items)
childViewModel.Update();
}
private void TargetSelectionViewModelOnPropertySelected(object? sender, DataModelInputDynamicEventArgs e)
{
ApplyList();
}
}
}

View File

@ -0,0 +1,94 @@
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:utilities="clr-namespace:Artemis.UI.Utilities"
xmlns:conditions="clr-namespace:Artemis.UI.Screens.ProfileEditor.Conditions"
x:Class="Artemis.UI.Screens.ProfileEditor.Conditions.DataModelConditionPredicateView"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance IsDesignTimeCreatable=False, Type={x:Type conditions:DataModelConditionPredicateViewModel}}">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DataModelConditions.xaml" />
<ResourceDictionary>
<utilities:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Margin="0 3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Grid.Row="0"
Grid.Column="0"
ToolTip="Delete the predicate"
Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Left"
Foreground="#E74C4C"
Width="25"
Height="25"
Command="{s:Action Delete}">
<materialDesign:PackIcon Kind="Close" Width="18" Height="18" />
</Button>
<!-- Left side, always a property -->
<ContentControl Grid.Row="0"
Grid.Column="1"
s:View.Model="{Binding LeftSideSelectionViewModel}"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsTabStop="False" />
<!-- Operator -->
<Button Grid.Row="0"
Grid.Column="2"
Style="{StaticResource DataModelConditionButtonLeftClickMenu}"
Background="#7B7B7B"
BorderBrush="#7B7B7B"
Content="{Binding SelectedOperator.Description}"
Click="PropertyButton_OnClick">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding Operators}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="{Binding Icon}" VerticalAlignment="Center" Margin="0 0 15 0" />
<TextBlock Text="{Binding Description}" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
</ContextMenu.ItemTemplate>
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MaterialDesignMenuItem}">
<Setter Property="Command" Value="{Binding Data.SelectOperatorCommand, Source={StaticResource DataContextProxy}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
<Setter Property="CommandTarget" Value="{Binding}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
</Button>
<!-- Right side, either a selection or an input -->
<ContentControl Grid.Row="0"
Grid.Column="3"
s:View.Model="{Binding RightSideSelectionViewModel}"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsTabStop="False" />
<ContentControl Grid.Row="0"
Grid.Column="3"
s:View.Model="{Binding RightSideInputViewModel}"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsTabStop="False" />
</Grid>
</UserControl>

View File

@ -1,14 +1,14 @@
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions namespace Artemis.UI.Screens.ProfileEditor.Conditions
{ {
/// <summary> /// <summary>
/// Interaction logic for DisplayConditionPredicateView.xaml /// Interaction logic for DataModelConditionPredicateView.xaml
/// </summary> /// </summary>
public partial class DisplayConditionPredicateView : UserControl public partial class DataModelConditionPredicateView : UserControl
{ {
public DisplayConditionPredicateView() public DataModelConditionPredicateView()
{ {
InitializeComponent(); InitializeComponent();
} }

View File

@ -0,0 +1,248 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Input;
using Artemis.UI.Shared.Services;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.Conditions
{
public class DataModelConditionPredicateViewModel : DataModelConditionViewModel, IDisposable
{
private readonly IConditionOperatorService _conditionOperatorService;
private readonly IDataModelUIService _dataModelUIService;
private readonly IProfileEditorService _profileEditorService;
private DataModelDynamicViewModel _leftSideSelectionViewModel;
private BindableCollection<ConditionOperator> _operators;
private DataModelStaticViewModel _rightSideInputViewModel;
private DataModelDynamicViewModel _rightSideSelectionViewModel;
private ConditionOperator _selectedOperator;
private List<Type> _supportedInputTypes;
public DataModelConditionPredicateViewModel(
DataModelConditionPredicate dataModelConditionPredicate,
IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService,
IConditionOperatorService conditionOperatorService,
ISettingsService settingsService) : base(dataModelConditionPredicate)
{
_profileEditorService = profileEditorService;
_dataModelUIService = dataModelUIService;
_conditionOperatorService = conditionOperatorService;
_supportedInputTypes = new List<Type>();
SelectOperatorCommand = new DelegateCommand(ExecuteSelectOperatorCommand);
Operators = new BindableCollection<ConditionOperator>();
ShowDataModelValues = settingsService.GetSetting<bool>("ProfileEditor.ShowDataModelValues");
Initialize();
}
public DataModelConditionPredicate DataModelConditionPredicate => (DataModelConditionPredicate) Model;
public PluginSetting<bool> ShowDataModelValues { get; }
public BindableCollection<ConditionOperator> Operators
{
get => _operators;
set => SetAndNotify(ref _operators, value);
}
public DataModelDynamicViewModel LeftSideSelectionViewModel
{
get => _leftSideSelectionViewModel;
set => SetAndNotify(ref _leftSideSelectionViewModel, value);
}
public ConditionOperator SelectedOperator
{
get => _selectedOperator;
set => SetAndNotify(ref _selectedOperator, value);
}
public DataModelDynamicViewModel RightSideSelectionViewModel
{
get => _rightSideSelectionViewModel;
set => SetAndNotify(ref _rightSideSelectionViewModel, value);
}
public DataModelStaticViewModel RightSideInputViewModel
{
get => _rightSideInputViewModel;
set => SetAndNotify(ref _rightSideInputViewModel, value);
}
public DelegateCommand SelectOperatorCommand { get; }
public void Dispose()
{
if (LeftSideSelectionViewModel != null)
{
LeftSideSelectionViewModel.PropertySelected -= LeftSideOnPropertySelected;
LeftSideSelectionViewModel.Dispose();
LeftSideSelectionViewModel = null;
}
DisposeRightSideStatic();
DisposeRightSideDynamic();
}
public override void Delete()
{
base.Delete();
_profileEditorService.UpdateSelectedProfileElement();
}
public void Initialize()
{
LeftSideSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule());
LeftSideSelectionViewModel.PropertySelected += LeftSideOnPropertySelected;
// Determine which types are currently supported
var editors = _dataModelUIService.RegisteredDataModelEditors;
_supportedInputTypes = editors.Select(e => e.SupportedType).ToList();
_supportedInputTypes.AddRange(editors.Where(e => e.CompatibleConversionTypes != null).SelectMany(e => e.CompatibleConversionTypes));
Update();
}
public override void Update()
{
LeftSideSelectionViewModel.FilterTypes = _supportedInputTypes.ToArray();
LeftSideSelectionViewModel.PopulateSelectedPropertyViewModel(
DataModelConditionPredicate.LeftDataModel,
DataModelConditionPredicate.LeftPropertyPath
);
var leftSideType = LeftSideSelectionViewModel.SelectedPropertyViewModel?.PropertyInfo?.PropertyType;
// Get the supported operators
Operators.Clear();
Operators.AddRange(_conditionOperatorService.GetConditionOperatorsForType(leftSideType));
if (DataModelConditionPredicate.Operator == null)
DataModelConditionPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType)));
SelectedOperator = DataModelConditionPredicate.Operator;
// Ensure the right side has the proper VM
var targetType = LeftSideSelectionViewModel?.SelectedPropertyViewModel?.PropertyInfo?.PropertyType;
if (DataModelConditionPredicate.PredicateType == ProfileRightSideType.Dynamic)
{
DisposeRightSideStatic();
if (RightSideSelectionViewModel == null)
{
RightSideSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule());
RightSideSelectionViewModel.ButtonBrush = (Brush) Application.Current.FindResource("PrimaryHueMidBrush");
RightSideSelectionViewModel.PropertySelected += RightSideOnPropertySelected;
}
RightSideSelectionViewModel.PopulateSelectedPropertyViewModel(
DataModelConditionPredicate.RightDataModel,
DataModelConditionPredicate.RightPropertyPath
);
RightSideSelectionViewModel.FilterTypes = new[] {targetType};
}
else
{
DisposeRightSideDynamic();
if (RightSideInputViewModel == null)
{
RightSideInputViewModel = _dataModelUIService.GetStaticInputViewModel(targetType);
RightSideInputViewModel.ButtonBrush = (Brush) Application.Current.FindResource("PrimaryHueMidBrush");
RightSideInputViewModel.ValueUpdated += RightSideOnValueEntered;
}
if (RightSideInputViewModel.TargetType != targetType)
RightSideInputViewModel.UpdateTargetType(targetType);
}
}
public void ApplyLeftSide()
{
DataModelConditionPredicate.UpdateLeftSide(
LeftSideSelectionViewModel.SelectedPropertyViewModel.DataModel,
LeftSideSelectionViewModel.SelectedPropertyViewModel.PropertyPath
);
_profileEditorService.UpdateSelectedProfileElement();
SelectedOperator = DataModelConditionPredicate.Operator;
Update();
}
public void ApplyRightSideDynamic()
{
DataModelConditionPredicate.UpdateRightSide(
RightSideSelectionViewModel.SelectedPropertyViewModel.DataModel,
RightSideSelectionViewModel.SelectedPropertyViewModel.PropertyPath
);
_profileEditorService.UpdateSelectedProfileElement();
Update();
}
public void ApplyRightSideStatic(object value)
{
DataModelConditionPredicate.UpdateRightSide(value);
_profileEditorService.UpdateSelectedProfileElement();
Update();
}
public void ApplyOperator()
{
DataModelConditionPredicate.UpdateOperator(SelectedOperator);
_profileEditorService.UpdateSelectedProfileElement();
Update();
}
private void LeftSideOnPropertySelected(object sender, DataModelInputDynamicEventArgs e)
{
ApplyLeftSide();
}
private void RightSideOnPropertySelected(object? sender, DataModelInputDynamicEventArgs e)
{
ApplyRightSideDynamic();
}
private void RightSideOnValueEntered(object sender, DataModelInputStaticEventArgs e)
{
ApplyRightSideStatic(e.Value);
}
private void ExecuteSelectOperatorCommand(object context)
{
if (!(context is ConditionOperator DataModelConditionOperator))
return;
SelectedOperator = DataModelConditionOperator;
ApplyOperator();
}
private void DisposeRightSideStatic()
{
if (RightSideInputViewModel != null)
{
RightSideInputViewModel.ValueUpdated -= RightSideOnValueEntered;
RightSideInputViewModel.Dispose();
RightSideInputViewModel = null;
}
}
private void DisposeRightSideDynamic()
{
if (RightSideSelectionViewModel != null)
{
RightSideSelectionViewModel.PropertySelected -= RightSideOnPropertySelected;
RightSideSelectionViewModel.Dispose();
RightSideSelectionViewModel = null;
}
}
}
}

View File

@ -1,23 +0,0 @@
using Artemis.Core;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract
{
public abstract class DisplayConditionViewModel : Conductor<DisplayConditionViewModel>.Collection.AllActive
{
protected DisplayConditionViewModel(DataModelConditionPart model)
{
Model = model;
}
public DataModelConditionPart Model { get; }
public abstract void Update();
public virtual void Delete()
{
Model.Parent.RemoveChild(Model);
((DisplayConditionViewModel) Parent).Update();
}
}
}

View File

@ -1,186 +0,0 @@
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:utilities="clr-namespace:Artemis.UI.Utilities"
xmlns:displayConditions="clr-namespace:Artemis.UI.Screens.ProfileEditor.DisplayConditions"
x:Class="Artemis.UI.Screens.ProfileEditor.DisplayConditions.DisplayConditionListPredicateView"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance IsDesignTimeCreatable=False, Type={x:Type displayConditions:DisplayConditionListPredicateViewModel}}">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DisplayConditions.xaml" />
<ResourceDictionary>
<converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<utilities:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
<DataTemplate x:Key="DataModelDataTemplate">
<Control x:Name="TemplateControl" Focusable="False" Template="{StaticResource DataModelSelectionTemplate}" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Data.ShowDataModelValues.Value, Source={StaticResource DataContextProxy}}" Value="True">
<Setter TargetName="TemplateControl" Property="Template" Value="{StaticResource DataModelSelectionTemplateWithValues}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Margin="0 3" Visibility="{Binding IsInitialized, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Grid.Row="0"
Grid.Column="0"
ToolTip="Delete the predicate"
Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Left"
Foreground="#E74C4C"
Width="25"
Height="25"
Command="{s:Action Delete}">
<materialDesign:PackIcon Kind="Close" Width="18" Height="18" />
</Button>
<!-- Left side, always a property -->
<Button Grid.Row="0"
Grid.Column="1"
Background="#476CBC"
BorderBrush="#476CBC"
Style="{StaticResource DisplayConditionButton}"
ToolTip="{Binding SelectedLeftSideProperty.DisplayPropertyPath}"
Click="PropertyButton_OnClick">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding LeftSideDataModel.Children}" IsOpen="{Binding LeftSideDataModelOpen, Mode=OneWayToSource}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MaterialDesignMenuItem}">
<Setter Property="ItemsSource" Value="{Binding Children}" />
<Setter Property="Command" Value="{Binding Data.SelectLeftPropertyCommand, Source={StaticResource DataContextProxy}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
<Setter Property="CommandTarget" Value="{Binding}" />
<Setter Property="IsEnabled" Value="{Binding IsMatchingFilteredTypes}" />
<Setter Property="IsSubmenuOpen" Value="{Binding IsVisualizationExpanded, Mode=TwoWay}" />
<Setter Property="HeaderTemplate" Value="{StaticResource DataModelDataTemplate}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
<Grid>
<TextBlock Text="{Binding SelectedLeftSideProperty.PropertyDescription.Name}"
Visibility="{Binding SelectedLeftSideProperty, Converter={StaticResource NullToVisibilityConverter}}" />
<TextBlock Text="« Select a property »"
FontStyle="Italic"
Visibility="{Binding SelectedLeftSideProperty, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}" />
</Grid>
</Button>
<!-- Operator -->
<Button Grid.Row="0"
Grid.Column="2"
Style="{StaticResource DisplayConditionButtonLeftClickMenu}"
Background="#7B7B7B"
BorderBrush="#7B7B7B"
Content="{Binding SelectedOperator.Description}"
Click="PropertyButton_OnClick">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding Operators}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="{Binding Icon}" VerticalAlignment="Center" Margin="0 0 15 0" />
<TextBlock Text="{Binding Description}" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
</ContextMenu.ItemTemplate>
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MaterialDesignMenuItem}">
<Setter Property="Command" Value="{Binding Data.SelectOperatorCommand, Source={StaticResource DataContextProxy}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
<Setter Property="CommandTarget" Value="{Binding}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
</Button>
<Grid Grid.Row="0"
Grid.Column="3"
Visibility="{Binding SelectedOperator.SupportsRightSide, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
<!-- Right side property if type is dynamic -->
<Button Background="{DynamicResource PrimaryHueMidBrush}"
BorderBrush="{DynamicResource PrimaryHueMidBrush}"
Style="{StaticResource DisplayConditionButton}"
ToolTip="{Binding SelectedRightSideProperty.DisplayPropertyPath}"
Click="PropertyButton_OnClick"
Visibility="{Binding ShowRightSidePropertySelection, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding RightSideDataModel.Children}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MaterialDesignMenuItem}">
<Setter Property="ItemsSource" Value="{Binding Children}" />
<Setter Property="Command" Value="{Binding Data.SelectRightPropertyCommand, Source={StaticResource DataContextProxy}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
<Setter Property="CommandTarget" Value="{Binding}" />
<Setter Property="IsEnabled" Value="{Binding IsMatchingFilteredTypes}" />
<Setter Property="IsSubmenuOpen" Value="{Binding IsVisualizationExpanded, Mode=TwoWay}" />
<Setter Property="HeaderTemplate" Value="{StaticResource DataModelDataTemplate}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
<Grid>
<TextBlock Text="{Binding SelectedRightSideProperty.PropertyDescription.Name}"
Visibility="{Binding SelectedRightSideProperty, Converter={StaticResource NullToVisibilityConverter}}" />
<TextBlock Text="« Select a property »"
FontStyle="Italic"
Visibility="{Binding SelectedRightSideProperty, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}" />
</Grid>
</Button>
<!-- Right side property if type is static -->
<materialDesign:Transitioner SelectedIndex="{Binding RightSideTransitionIndex}"
DefaultTransitionOrigin="0.5, 0.5"
Margin="3 -4"
Visibility="{Binding ShowRightSidePropertySelection, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}">
<Button Style="{StaticResource DisplayConditionButton}"
Background="{DynamicResource PrimaryHueMidBrush}"
BorderBrush="{DynamicResource PrimaryHueMidBrush}"
Margin="0 4"
Command="{s:Action ActivateRightSideInputViewModel}"
HorizontalAlignment="Left">
<Grid>
<StackPanel Visibility="{Binding RightStaticValue, Converter={StaticResource NullToVisibilityConverter}}" Orientation="Horizontal">
<TextBlock FontWeight="Light"
Text="{Binding SelectedLeftSideProperty.PropertyDescription.Prefix}"
Visibility="{Binding SelectedLeftSideProperty.PropertyDescription.Prefix, Converter={StaticResource NullToVisibilityConverter}}" />
<TextBlock Text="{Binding RightStaticValue}" />
<TextBlock FontWeight="Light"
Text="{Binding SelectedLeftSideProperty.PropertyDescription.Affix}"
Visibility="{Binding SelectedLeftSideProperty.PropertyDescription.Affix, Converter={StaticResource NullToVisibilityConverter}}" />
</StackPanel>
<TextBlock Text="« Enter a value »"
FontStyle="Italic"
Visibility="{Binding RightStaticValue, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}" />
</Grid>
</Button>
<Border BorderBrush="{DynamicResource PrimaryHueMidBrush}"
Background="{DynamicResource MaterialDesignPaper}"
CornerRadius="3"
Padding="3"
HorizontalAlignment="Left"
MinWidth="140">
<ContentControl s:View.Model="{Binding RightSideInputViewModel}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
</Border>
</materialDesign:Transitioner>
</Grid>
</Grid>
</UserControl>

View File

@ -1,353 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Timers;
using System.Windows;
using System.Windows.Input;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Events;
using Artemis.UI.Exceptions;
using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Artemis.UI.Utilities;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
{
public class DisplayConditionListPredicateViewModel : DisplayConditionViewModel, IHandle<MainWindowKeyEvent>, IHandle<MainWindowMouseEvent>, IDisposable
{
private readonly IConditionOperatorService _conditionOperatorService;
private readonly IDataModelUIService _dataModelUIService;
private readonly IEventAggregator _eventAggregator;
private readonly IProfileEditorService _profileEditorService;
private readonly Timer _updateTimer;
private bool _isInitialized;
private DataModelVisualizationViewModel _leftSideDataModel;
private BindableCollection<ConditionOperator> _operators;
private DataModelVisualizationViewModel _rightSideDataModel;
private DataModelInputViewModel _rightSideInputViewModel;
private int _rightSideTransitionIndex;
private object _rightStaticValue;
private DataModelVisualizationViewModel _selectedLeftSideProperty;
private ConditionOperator _selectedOperator;
private DataModelVisualizationViewModel _selectedRightSideProperty;
private List<Type> _supportedInputTypes;
public DisplayConditionListPredicateViewModel(
DataModelConditionListPredicate dataModelConditionListPredicate,
IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService,
IConditionOperatorService conditionOperatorService,
ISettingsService settingsService,
IEventAggregator eventAggregator) : base(dataModelConditionListPredicate)
{
_profileEditorService = profileEditorService;
_dataModelUIService = dataModelUIService;
_conditionOperatorService = conditionOperatorService;
_eventAggregator = eventAggregator;
_updateTimer = new Timer(500);
_supportedInputTypes = new List<Type>();
SelectLeftPropertyCommand = new DelegateCommand(ExecuteSelectLeftProperty);
SelectRightPropertyCommand = new DelegateCommand(ExecuteSelectRightProperty);
SelectOperatorCommand = new DelegateCommand(ExecuteSelectOperatorCommand);
Operators = new BindableCollection<ConditionOperator>();
ShowDataModelValues = settingsService.GetSetting<bool>("ProfileEditor.ShowDataModelValues");
// Initialize async, no need to wait for it
Task.Run(Initialize);
}
public DataModelConditionListPredicate DataModelConditionListPredicate => (DataModelConditionListPredicate) Model;
public bool ShowRightSidePropertySelection => DataModelConditionListPredicate.PredicateType == ProfileRightSideType.Dynamic;
public bool CanActivateRightSideInputViewModel => SelectedLeftSideProperty?.PropertyInfo != null;
public PluginSetting<bool> ShowDataModelValues { get; }
public bool IsInitialized
{
get => _isInitialized;
private set => SetAndNotify(ref _isInitialized, value);
}
public bool LeftSideDataModelOpen { get; set; }
public DataModelVisualizationViewModel LeftSideDataModel
{
get => _leftSideDataModel;
set => SetAndNotify(ref _leftSideDataModel, value);
}
public DataModelVisualizationViewModel RightSideDataModel
{
get => _rightSideDataModel;
set => SetAndNotify(ref _rightSideDataModel, value);
}
public bool RightSideDataModelOpen { get; set; }
public DataModelVisualizationViewModel SelectedLeftSideProperty
{
get => _selectedLeftSideProperty;
set
{
if (!SetAndNotify(ref _selectedLeftSideProperty, value)) return;
NotifyOfPropertyChange(nameof(CanActivateRightSideInputViewModel));
}
}
public DataModelVisualizationViewModel SelectedRightSideProperty
{
get => _selectedRightSideProperty;
set => SetAndNotify(ref _selectedRightSideProperty, value);
}
public object RightStaticValue
{
get => _rightStaticValue;
set => SetAndNotify(ref _rightStaticValue, value);
}
public int RightSideTransitionIndex
{
get => _rightSideTransitionIndex;
set => SetAndNotify(ref _rightSideTransitionIndex, value);
}
public DataModelInputViewModel RightSideInputViewModel
{
get => _rightSideInputViewModel;
set => SetAndNotify(ref _rightSideInputViewModel, value);
}
public BindableCollection<ConditionOperator> Operators
{
get => _operators;
set => SetAndNotify(ref _operators, value);
}
public ConditionOperator SelectedOperator
{
get => _selectedOperator;
set => SetAndNotify(ref _selectedOperator, value);
}
public DelegateCommand SelectLeftPropertyCommand { get; }
public DelegateCommand SelectRightPropertyCommand { get; }
public DelegateCommand SelectOperatorCommand { get; }
public void Handle(MainWindowKeyEvent message)
{
if (RightSideInputViewModel == null)
return;
if (!message.KeyDown && message.EventArgs.Key == Key.Escape)
RightSideInputViewModel.Cancel();
if (!message.KeyDown && message.EventArgs.Key == Key.Enter)
RightSideInputViewModel.Submit();
}
public void Handle(MainWindowMouseEvent message)
{
if (RightSideInputViewModel == null)
return;
if (message.Sender is FrameworkElement frameworkElement && !frameworkElement.IsDescendantOf(RightSideInputViewModel.View))
RightSideInputViewModel.Submit();
}
public override void Delete()
{
base.Delete();
_profileEditorService.UpdateSelectedProfileElement();
}
public void Initialize()
{
// Get the data models
LeftSideDataModel = GetListDataModel();
RightSideDataModel = GetListDataModel();
LeftSideDataModel.UpdateRequested += LeftDataModelUpdateRequested;
RightSideDataModel.UpdateRequested += RightDataModelUpdateRequested;
// Determine which types are currently supported
var editors = _dataModelUIService.RegisteredDataModelEditors;
_supportedInputTypes = editors.Select(e => e.SupportedType).ToList();
_supportedInputTypes.AddRange(editors.Where(e => e.CompatibleConversionTypes != null).SelectMany(e => e.CompatibleConversionTypes));
Update();
_updateTimer.Start();
_updateTimer.Elapsed += OnUpdateTimerOnElapsed;
IsInitialized = true;
}
public override void Update()
{
// Not yet initialized if these are null
if (LeftSideDataModel == null || RightSideDataModel == null)
return;
var listDataModelGuid = DataModelConditionListPredicate.ListDataModel.PluginInfo.Guid;
// If static, only allow selecting properties also supported by input
if (DataModelConditionListPredicate.PredicateType == ProfileRightSideType.Static)
LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray());
// Determine the left side property first
SelectedLeftSideProperty = LeftSideDataModel.GetChildByPath(listDataModelGuid, DataModelConditionListPredicate.LeftPropertyPath);
var leftSideType = SelectedLeftSideProperty?.PropertyInfo?.PropertyType;
// Get the supported operators
Operators.Clear();
Operators.AddRange(_conditionOperatorService.GetConditionOperatorsForType(leftSideType));
if (DataModelConditionListPredicate.Operator == null)
DataModelConditionListPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType)));
SelectedOperator = DataModelConditionListPredicate.Operator;
// Determine the right side
if (DataModelConditionListPredicate.PredicateType == ProfileRightSideType.Dynamic)
{
SelectedRightSideProperty = RightSideDataModel.GetChildByPath(listDataModelGuid, DataModelConditionListPredicate.RightPropertyPath);
RightSideDataModel.ApplyTypeFilter(true, leftSideType);
}
else
RightStaticValue = DataModelConditionListPredicate.RightStaticValue;
}
public void ApplyLeftSide()
{
DataModelConditionListPredicate.UpdateLeftSide(SelectedLeftSideProperty.PropertyPath);
_profileEditorService.UpdateSelectedProfileElement();
SelectedOperator = DataModelConditionListPredicate.Operator;
Update();
}
public void ApplyRightSideDynamic()
{
DataModelConditionListPredicate.UpdateRightSideDynamic(SelectedRightSideProperty.PropertyPath);
_profileEditorService.UpdateSelectedProfileElement();
Update();
}
public void ApplyRightSideStatic(object value, bool isSubmitted)
{
if (isSubmitted)
{
DataModelConditionListPredicate.UpdateRightSideStatic(value);
_profileEditorService.UpdateSelectedProfileElement();
Update();
}
RightSideTransitionIndex = 0;
RightSideInputViewModel = null;
RightStaticValue = value;
_eventAggregator.Unsubscribe(this);
}
public void ApplyOperator()
{
DataModelConditionListPredicate.UpdateOperator(SelectedOperator);
_profileEditorService.UpdateSelectedProfileElement();
Update();
}
public void ActivateRightSideInputViewModel()
{
if (SelectedLeftSideProperty?.PropertyInfo == null)
return;
RightSideTransitionIndex = 1;
RightSideInputViewModel = _dataModelUIService.GetDataModelInputViewModel(
SelectedLeftSideProperty.PropertyInfo.PropertyType,
SelectedLeftSideProperty.PropertyDescription,
DataModelConditionListPredicate.RightStaticValue,
ApplyRightSideStatic
);
_eventAggregator.Subscribe(this);
}
private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
{
if (LeftSideDataModelOpen)
LeftSideDataModel.Update(_dataModelUIService);
else if (RightSideDataModelOpen)
RightSideDataModel.Update(_dataModelUIService);
}
private void RightDataModelUpdateRequested(object sender, EventArgs e)
{
var listDataModelGuid = DataModelConditionListPredicate.ListDataModel.PluginInfo.Guid;
var leftSideType = SelectedLeftSideProperty?.PropertyInfo?.PropertyType;
// If the right side property is missing it may be available now that the data model has been updated
if (SelectedRightSideProperty == null && DataModelConditionListPredicate.RightPropertyPath != null)
SelectedRightSideProperty = RightSideDataModel.GetChildByPath(listDataModelGuid, DataModelConditionListPredicate.RightPropertyPath);
// With the data model updated, also reapply the filter
RightSideDataModel.ApplyTypeFilter(true, leftSideType);
}
private void LeftDataModelUpdateRequested(object sender, EventArgs e)
{
if (DataModelConditionListPredicate.PredicateType == ProfileRightSideType.Static)
LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray());
}
private DataModelVisualizationViewModel GetListDataModel()
{
if (DataModelConditionListPredicate.ListDataModel == null || DataModelConditionListPredicate.ListPropertyPath == null)
throw new ArtemisUIException("Cannot create a list predicate without first selecting a target list");
var dataModel = _dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule(), true);
var listDataModel = (DataModelListViewModel) dataModel.GetChildByPath(
DataModelConditionListPredicate.ListDataModel.PluginInfo.Guid,
DataModelConditionListPredicate.ListPropertyPath
);
return listDataModel.GetListTypeViewModel(_dataModelUIService);
}
private void ExecuteSelectLeftProperty(object context)
{
if (!(context is DataModelVisualizationViewModel dataModelVisualizationViewModel))
return;
SelectedLeftSideProperty = dataModelVisualizationViewModel;
ApplyLeftSide();
}
private void ExecuteSelectRightProperty(object context)
{
if (!(context is DataModelVisualizationViewModel dataModelVisualizationViewModel))
return;
SelectedRightSideProperty = dataModelVisualizationViewModel;
ApplyRightSideDynamic();
}
private void ExecuteSelectOperatorCommand(object context)
{
if (!(context is ConditionOperator displayConditionOperator))
return;
SelectedOperator = displayConditionOperator;
ApplyOperator();
}
public void Dispose()
{
_updateTimer.Dispose();
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
}
}
}

View File

@ -1,122 +0,0 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.DisplayConditions.DisplayConditionListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.DisplayConditions"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:utilities="clr-namespace:Artemis.UI.Utilities"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance Type=local:DisplayConditionListViewModel, IsDesignTimeCreatable=False}">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DisplayConditions.xaml" />
<ResourceDictionary>
<converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<utilities:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
<DataTemplate x:Key="DataModelDataTemplate">
<Control x:Name="TemplateControl" Focusable="False" Template="{StaticResource DataModelSelectionTemplate}" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Data.ShowDataModelValues.Value, Source={StaticResource DataContextProxy}}" Value="True">
<Setter TargetName="TemplateControl" Property="Template" Value="{StaticResource DataModelSelectionTemplateWithValues}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Margin="0 3">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Row="0"
Grid.Column="0"
ToolTip="Delete the list predicate and all its children"
Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Left"
Foreground="#E74C4C"
Width="25"
Height="25"
Command="{s:Action Delete}">
<materialDesign:PackIcon Kind="Close" Width="18" Height="18" />
</Button>
<!-- Left side, the list this predicate is targeting -->
<Button Grid.Row="0"
Grid.Column="1"
Background="#476CBC"
BorderBrush="#476CBC"
Style="{StaticResource DisplayConditionButton}"
ToolTip="{Binding SelectedListProperty.DisplayPropertyPath}"
Click="PropertyButton_OnClick"
Visibility="{Binding IsInitialized, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding TargetDataModel.Children}" IsOpen="{Binding TargetDataModelOpen, Mode=OneWayToSource}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MaterialDesignMenuItem}">
<Setter Property="ItemsSource" Value="{Binding Children}" />
<Setter Property="Command" Value="{Binding Data.SelectListPropertyCommand, Source={StaticResource DataContextProxy}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
<Setter Property="CommandTarget" Value="{Binding}" />
<Setter Property="IsEnabled" Value="{Binding IsMatchingFilteredTypes}" />
<Setter Property="IsSubmenuOpen" Value="{Binding IsVisualizationExpanded, Mode=TwoWay}" />
<Setter Property="HeaderTemplate" Value="{StaticResource DataModelDataTemplate}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
<Grid>
<TextBlock Text="{Binding SelectedListProperty.PropertyDescription.Name}"
Visibility="{Binding SelectedListProperty, Converter={StaticResource NullToVisibilityConverter}}" />
<TextBlock Text="« Select a list »"
FontStyle="Italic"
Visibility="{Binding SelectedListProperty, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}" />
</Grid>
</Button>
<!-- Operator -->
<Button Grid.Row="0"
Grid.Column="2"
Style="{StaticResource DisplayConditionButtonLeftClickMenu}"
Background="#7B7B7B"
BorderBrush="#7B7B7B"
Content="{Binding SelectedListOperator}"
Click="PropertyButton_OnClick"
HorizontalAlignment="Left"
Visibility="{Binding IsInitialized, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Any" Command="{s:Action SelectListOperator}" CommandParameter="Any" />
<MenuItem Header="All" Command="{s:Action SelectListOperator}" CommandParameter="All" />
<MenuItem Header="None" Command="{s:Action SelectListOperator}" CommandParameter="None" />
<MenuItem Header="Count (NYI)" Command="{s:Action SelectListOperator}" CommandParameter="Count" IsEnabled="False" />
</ContextMenu>
</Button.ContextMenu>
</Button>
<ItemsControl Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" ItemsSource="{Binding Items}" Margin="0 4 0 0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<materialDesign:TransitioningContent>
<materialDesign:TransitioningContent.OpeningEffects>
<materialDesign:TransitionEffect Kind="FadeIn" />
<materialDesign:TransitionEffect Kind="SlideInFromLeft" />
</materialDesign:TransitioningContent.OpeningEffects>
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
</materialDesign:TransitioningContent>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>

View File

@ -1,203 +0,0 @@
using System;
using System.Collections;
using System.Linq;
using System.Threading.Tasks;
using System.Timers;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Artemis.UI.Utilities;
using Humanizer;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
{
public class DisplayConditionListViewModel : DisplayConditionViewModel, IDisposable
{
private readonly IDataModelUIService _dataModelUIService;
private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory;
private readonly IProfileEditorService _profileEditorService;
private readonly Timer _updateTimer;
private bool _isInitialized;
private DataModelListViewModel _selectedListProperty;
private DataModelPropertiesViewModel _targetDataModel;
public DisplayConditionListViewModel(
DataModelConditionList dataModelConditionList,
DisplayConditionViewModel parent,
IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService,
IDisplayConditionsVmFactory displayConditionsVmFactory,
ISettingsService settingsService) : base(dataModelConditionList)
{
_profileEditorService = profileEditorService;
_dataModelUIService = dataModelUIService;
_displayConditionsVmFactory = displayConditionsVmFactory;
_updateTimer = new Timer(500);
SelectListPropertyCommand = new DelegateCommand(ExecuteSelectListProperty);
ShowDataModelValues = settingsService.GetSetting<bool>("ProfileEditor.ShowDataModelValues");
// Initialize async, no need to wait for it
Task.Run(Initialize);
}
public DelegateCommand SelectListPropertyCommand { get; }
public PluginSetting<bool> ShowDataModelValues { get; }
public DataModelConditionList DataModelConditionList => (DataModelConditionList) Model;
public bool IsInitialized
{
get => _isInitialized;
set => SetAndNotify(ref _isInitialized, value);
}
public bool TargetDataModelOpen { get; set; }
public DataModelPropertiesViewModel TargetDataModel
{
get => _targetDataModel;
set => SetAndNotify(ref _targetDataModel, value);
}
public DataModelListViewModel SelectedListProperty
{
get => _selectedListProperty;
set => SetAndNotify(ref _selectedListProperty, value);
}
public string SelectedListOperator => DataModelConditionList.ListOperator.Humanize();
public void SelectListOperator(string type)
{
var enumValue = Enum.Parse<ListOperator>(type);
DataModelConditionList.ListOperator = enumValue;
NotifyOfPropertyChange(nameof(SelectedListOperator));
_profileEditorService.UpdateSelectedProfileElement();
}
public void AddCondition(string type)
{
if (type == "Static")
DataModelConditionList.AddChild(new DataModelConditionPredicate(DataModelConditionList, ProfileRightSideType.Static));
else if (type == "Dynamic")
DataModelConditionList.AddChild(new DataModelConditionPredicate(DataModelConditionList, ProfileRightSideType.Dynamic));
Update();
_profileEditorService.UpdateSelectedProfileElement();
}
public void AddGroup()
{
DataModelConditionList.AddChild(new DataModelConditionGroup(DataModelConditionList));
Update();
_profileEditorService.UpdateSelectedProfileElement();
}
public override void Delete()
{
base.Delete();
_profileEditorService.UpdateSelectedProfileElement();
}
public void Initialize()
{
// Get the data models
TargetDataModel = _dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule(), true);
TargetDataModel.UpdateRequested += TargetDataModelUpdateRequested;
Update();
_updateTimer.Start();
_updateTimer.Elapsed += OnUpdateTimerOnElapsed;
IsInitialized = true;
}
public void ApplyList()
{
DataModelConditionList.UpdateList(SelectedListProperty.DataModel, SelectedListProperty.PropertyPath);
_profileEditorService.UpdateSelectedProfileElement();
Update();
}
public override void Update()
{
if (TargetDataModel == null)
return;
NotifyOfPropertyChange(nameof(SelectedListOperator));
// Update the selected list property
if (DataModelConditionList.ListDataModel != null && DataModelConditionList.ListPropertyPath != null)
{
var child = TargetDataModel.GetChildByPath(
DataModelConditionList.ListDataModel.PluginInfo.Guid,
DataModelConditionList.ListPropertyPath
);
SelectedListProperty = child as DataModelListViewModel;
}
// Ensure filtering is applied to include Enumerables only
TargetDataModel.ApplyTypeFilter(true, typeof(IList));
// Remove VMs of effects no longer applied on the layer
var toRemove = Items.Where(c => !DataModelConditionList.Children.Contains(c.Model)).ToList();
// Using RemoveRange breaks our lovely animations
foreach (var displayConditionViewModel in toRemove)
Items.Remove(displayConditionViewModel);
foreach (var childModel in Model.Children)
{
if (Items.Any(c => c.Model == childModel))
continue;
if (!(childModel is DataModelConditionGroup displayConditionGroup))
continue;
var viewModel = _displayConditionsVmFactory.DisplayConditionGroupViewModel(displayConditionGroup, true);
viewModel.IsRootGroup = true;
Items.Add(viewModel);
}
foreach (var childViewModel in Items)
childViewModel.Update();
}
private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
{
if (TargetDataModelOpen)
{
TargetDataModel?.Update(_dataModelUIService);
SelectedListProperty?.Update(_dataModelUIService);
}
}
private void TargetDataModelUpdateRequested(object sender, EventArgs e)
{
TargetDataModel.ApplyTypeFilter(true, typeof(IList));
}
private void ExecuteSelectListProperty(object context)
{
if (!(context is DataModelListViewModel dataModelListViewModel))
return;
SelectedListProperty = dataModelListViewModel;
ApplyList();
}
public void Dispose()
{
_updateTimer.Dispose();
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
}
}
}

View File

@ -1,186 +0,0 @@
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:utilities="clr-namespace:Artemis.UI.Utilities"
xmlns:displayConditions="clr-namespace:Artemis.UI.Screens.ProfileEditor.DisplayConditions"
x:Class="Artemis.UI.Screens.ProfileEditor.DisplayConditions.DisplayConditionPredicateView"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance IsDesignTimeCreatable=False, Type={x:Type displayConditions:DisplayConditionPredicateViewModel}}">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DisplayConditions.xaml" />
<ResourceDictionary>
<converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<utilities:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
<DataTemplate x:Key="DataModelDataTemplate">
<Control x:Name="TemplateControl" Focusable="False" Template="{StaticResource DataModelSelectionTemplate}" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Data.ShowDataModelValues.Value, Source={StaticResource DataContextProxy}}" Value="True">
<Setter TargetName="TemplateControl" Property="Template" Value="{StaticResource DataModelSelectionTemplateWithValues}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Margin="0 3" Visibility="{Binding IsInitialized, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Grid.Row="0"
Grid.Column="0"
ToolTip="Delete the predicate"
Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Left"
Foreground="#E74C4C"
Width="25"
Height="25"
Command="{s:Action Delete}">
<materialDesign:PackIcon Kind="Close" Width="18" Height="18" />
</Button>
<!-- Left side, always a property -->
<Button Grid.Row="0"
Grid.Column="1"
Background="#ab47bc"
BorderBrush="#ab47bc"
Style="{StaticResource DisplayConditionButton}"
ToolTip="{Binding SelectedLeftSideProperty.DisplayPropertyPath}"
Click="PropertyButton_OnClick">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding LeftSideDataModel.Children}" IsOpen="{Binding LeftSideDataModelOpen, Mode=OneWayToSource}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MaterialDesignMenuItem}">
<Setter Property="ItemsSource" Value="{Binding Children}" />
<Setter Property="Command" Value="{Binding Data.SelectLeftPropertyCommand, Source={StaticResource DataContextProxy}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
<Setter Property="CommandTarget" Value="{Binding}" />
<Setter Property="IsEnabled" Value="{Binding IsMatchingFilteredTypes}" />
<Setter Property="IsSubmenuOpen" Value="{Binding IsVisualizationExpanded, Mode=TwoWay}" />
<Setter Property="HeaderTemplate" Value="{StaticResource DataModelDataTemplate}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
<Grid>
<TextBlock Text="{Binding SelectedLeftSideProperty.PropertyDescription.Name}"
Visibility="{Binding SelectedLeftSideProperty, Converter={StaticResource NullToVisibilityConverter}}" />
<TextBlock Text="« Select a property »"
FontStyle="Italic"
Visibility="{Binding SelectedLeftSideProperty, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}" />
</Grid>
</Button>
<!-- Operator -->
<Button Grid.Row="0"
Grid.Column="2"
Style="{StaticResource DisplayConditionButtonLeftClickMenu}"
Background="#7B7B7B"
BorderBrush="#7B7B7B"
Content="{Binding SelectedOperator.Description}"
Click="PropertyButton_OnClick">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding Operators}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="{Binding Icon}" VerticalAlignment="Center" Margin="0 0 15 0" />
<TextBlock Text="{Binding Description}" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
</ContextMenu.ItemTemplate>
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MaterialDesignMenuItem}">
<Setter Property="Command" Value="{Binding Data.SelectOperatorCommand, Source={StaticResource DataContextProxy}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
<Setter Property="CommandTarget" Value="{Binding}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
</Button>
<Grid Grid.Row="0"
Grid.Column="3"
Visibility="{Binding SelectedOperator.SupportsRightSide, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
<!-- Right side property if type is dynamic -->
<Button Background="{DynamicResource PrimaryHueMidBrush}"
BorderBrush="{DynamicResource PrimaryHueMidBrush}"
Style="{StaticResource DisplayConditionButton}"
ToolTip="{Binding SelectedRightSideProperty.DisplayPropertyPath}"
Click="PropertyButton_OnClick"
Visibility="{Binding ShowRightSidePropertySelection, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding RightSideDataModel.Children}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MaterialDesignMenuItem}">
<Setter Property="ItemsSource" Value="{Binding Children}" />
<Setter Property="Command" Value="{Binding Data.SelectRightPropertyCommand, Source={StaticResource DataContextProxy}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
<Setter Property="CommandTarget" Value="{Binding}" />
<Setter Property="IsEnabled" Value="{Binding IsMatchingFilteredTypes}" />
<Setter Property="IsSubmenuOpen" Value="{Binding IsVisualizationExpanded, Mode=TwoWay}" />
<Setter Property="HeaderTemplate" Value="{StaticResource DataModelDataTemplate}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
<Grid>
<TextBlock Text="{Binding SelectedRightSideProperty.PropertyDescription.Name}"
Visibility="{Binding SelectedRightSideProperty, Converter={StaticResource NullToVisibilityConverter}}" />
<TextBlock Text="« Select a property »"
FontStyle="Italic"
Visibility="{Binding SelectedRightSideProperty, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}" />
</Grid>
</Button>
<!-- Right side property if type is static -->
<materialDesign:Transitioner SelectedIndex="{Binding RightSideTransitionIndex}"
DefaultTransitionOrigin="0.5, 0.5"
Margin="3 -4"
Visibility="{Binding ShowRightSidePropertySelection, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}">
<Button Style="{StaticResource DisplayConditionButton}"
Background="{DynamicResource PrimaryHueMidBrush}"
BorderBrush="{DynamicResource PrimaryHueMidBrush}"
Margin="0 4"
Command="{s:Action ActivateRightSideInputViewModel}"
HorizontalAlignment="Left">
<Grid>
<StackPanel Visibility="{Binding RightStaticValue, Converter={StaticResource NullToVisibilityConverter}}" Orientation="Horizontal">
<TextBlock FontWeight="Light"
Text="{Binding SelectedLeftSideProperty.PropertyDescription.Prefix}"
Visibility="{Binding SelectedLeftSideProperty.PropertyDescription.Prefix, Converter={StaticResource NullToVisibilityConverter}}" />
<TextBlock Text="{Binding RightStaticValue}" />
<TextBlock FontWeight="Light"
Text="{Binding SelectedLeftSideProperty.PropertyDescription.Affix}"
Visibility="{Binding SelectedLeftSideProperty.PropertyDescription.Affix, Converter={StaticResource NullToVisibilityConverter}}" />
</StackPanel>
<TextBlock Text="« Enter a value »"
FontStyle="Italic"
Visibility="{Binding RightStaticValue, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}" />
</Grid>
</Button>
<Border BorderBrush="{DynamicResource PrimaryHueMidBrush}"
Background="{DynamicResource MaterialDesignPaper}"
CornerRadius="3"
Padding="3"
HorizontalAlignment="Left"
MinWidth="140">
<ContentControl s:View.Model="{Binding RightSideInputViewModel}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
</Border>
</materialDesign:Transitioner>
</Grid>
</Grid>
</UserControl>

View File

@ -1,332 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Timers;
using System.Windows;
using System.Windows.Input;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Events;
using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Artemis.UI.Utilities;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
{
public class DisplayConditionPredicateViewModel : DisplayConditionViewModel, IHandle<MainWindowKeyEvent>, IHandle<MainWindowMouseEvent>, IDisposable
{
private readonly IConditionOperatorService _conditionOperatorService;
private readonly IDataModelUIService _dataModelUIService;
private readonly IEventAggregator _eventAggregator;
private readonly IProfileEditorService _profileEditorService;
private readonly Timer _updateTimer;
private bool _isInitialized;
private DataModelPropertiesViewModel _leftSideDataModel;
private BindableCollection<ConditionOperator> _operators;
private DataModelPropertiesViewModel _rightSideDataModel;
private DataModelInputViewModel _rightSideInputViewModel;
private int _rightSideTransitionIndex;
private object _rightStaticValue;
private DataModelVisualizationViewModel _selectedLeftSideProperty;
private ConditionOperator _selectedOperator;
private DataModelVisualizationViewModel _selectedRightSideProperty;
private List<Type> _supportedInputTypes;
public DisplayConditionPredicateViewModel(
DataModelConditionPredicate dataModelConditionPredicate,
IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService,
IConditionOperatorService conditionOperatorService,
ISettingsService settingsService,
IEventAggregator eventAggregator) : base(dataModelConditionPredicate)
{
_profileEditorService = profileEditorService;
_dataModelUIService = dataModelUIService;
_conditionOperatorService = conditionOperatorService;
_eventAggregator = eventAggregator;
_updateTimer = new Timer(500);
_supportedInputTypes = new List<Type>();
SelectLeftPropertyCommand = new DelegateCommand(ExecuteSelectLeftProperty);
SelectRightPropertyCommand = new DelegateCommand(ExecuteSelectRightProperty);
SelectOperatorCommand = new DelegateCommand(ExecuteSelectOperatorCommand);
Operators = new BindableCollection<ConditionOperator>();
ShowDataModelValues = settingsService.GetSetting<bool>("ProfileEditor.ShowDataModelValues");
Initialize();
}
public DataModelConditionPredicate DataModelConditionPredicate => (DataModelConditionPredicate) Model;
public bool ShowRightSidePropertySelection => DataModelConditionPredicate.PredicateType == ProfileRightSideType.Dynamic;
public bool CanActivateRightSideInputViewModel => SelectedLeftSideProperty?.PropertyInfo != null;
public PluginSetting<bool> ShowDataModelValues { get; }
public bool IsInitialized
{
get => _isInitialized;
private set => SetAndNotify(ref _isInitialized, value);
}
public bool LeftSideDataModelOpen { get; set; }
public DataModelPropertiesViewModel LeftSideDataModel
{
get => _leftSideDataModel;
set => SetAndNotify(ref _leftSideDataModel, value);
}
public DataModelPropertiesViewModel RightSideDataModel
{
get => _rightSideDataModel;
set => SetAndNotify(ref _rightSideDataModel, value);
}
public bool RightSideDataModelOpen { get; set; }
public DataModelVisualizationViewModel SelectedLeftSideProperty
{
get => _selectedLeftSideProperty;
set
{
if (!SetAndNotify(ref _selectedLeftSideProperty, value)) return;
NotifyOfPropertyChange(nameof(CanActivateRightSideInputViewModel));
}
}
public DataModelVisualizationViewModel SelectedRightSideProperty
{
get => _selectedRightSideProperty;
set => SetAndNotify(ref _selectedRightSideProperty, value);
}
public object RightStaticValue
{
get => _rightStaticValue;
set => SetAndNotify(ref _rightStaticValue, value);
}
public int RightSideTransitionIndex
{
get => _rightSideTransitionIndex;
set => SetAndNotify(ref _rightSideTransitionIndex, value);
}
public DataModelInputViewModel RightSideInputViewModel
{
get => _rightSideInputViewModel;
set => SetAndNotify(ref _rightSideInputViewModel, value);
}
public BindableCollection<ConditionOperator> Operators
{
get => _operators;
set => SetAndNotify(ref _operators, value);
}
public ConditionOperator SelectedOperator
{
get => _selectedOperator;
set => SetAndNotify(ref _selectedOperator, value);
}
public DelegateCommand SelectLeftPropertyCommand { get; }
public DelegateCommand SelectRightPropertyCommand { get; }
public DelegateCommand SelectOperatorCommand { get; }
public void Handle(MainWindowKeyEvent message)
{
if (RightSideInputViewModel == null)
return;
if (!message.KeyDown && message.EventArgs.Key == Key.Escape)
RightSideInputViewModel.Cancel();
if (!message.KeyDown && message.EventArgs.Key == Key.Enter)
RightSideInputViewModel.Submit();
}
public void Handle(MainWindowMouseEvent message)
{
if (RightSideInputViewModel == null)
return;
if (message.Sender is FrameworkElement frameworkElement && !frameworkElement.IsDescendantOf(RightSideInputViewModel.View))
RightSideInputViewModel.Submit();
}
public override void Delete()
{
base.Delete();
_profileEditorService.UpdateSelectedProfileElement();
}
public void Initialize()
{
// Get the data models
LeftSideDataModel = _dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule(), true);
RightSideDataModel = _dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule(), true);
// Determine which types are currently supported
var editors = _dataModelUIService.RegisteredDataModelEditors;
_supportedInputTypes = editors.Select(e => e.SupportedType).ToList();
_supportedInputTypes.AddRange(editors.Where(e => e.CompatibleConversionTypes != null).SelectMany(e => e.CompatibleConversionTypes));
LeftSideDataModel.UpdateRequested += LeftDataModelUpdateRequested;
RightSideDataModel.UpdateRequested += RightDataModelUpdateRequested;
Update();
_updateTimer.Start();
_updateTimer.Elapsed += OnUpdateTimerOnElapsed;
IsInitialized = true;
}
public override void Update()
{
if (LeftSideDataModel == null || DataModelConditionPredicate.PredicateType == ProfileRightSideType.Dynamic && RightSideDataModel == null)
return;
// If static, only allow selecting properties also supported by input
if (DataModelConditionPredicate.PredicateType == ProfileRightSideType.Static)
LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray());
// Determine the left side property first
SelectedLeftSideProperty = LeftSideDataModel.GetChildForCondition(DataModelConditionPredicate, DisplayConditionSide.Left);
var leftSideType = SelectedLeftSideProperty?.PropertyInfo?.PropertyType;
// Get the supported operators
Operators.Clear();
Operators.AddRange(_conditionOperatorService.GetConditionOperatorsForType(leftSideType));
if (DataModelConditionPredicate.Operator == null)
DataModelConditionPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType)));
SelectedOperator = DataModelConditionPredicate.Operator;
// Determine the right side
if (DataModelConditionPredicate.PredicateType == ProfileRightSideType.Dynamic)
{
SelectedRightSideProperty = LeftSideDataModel.GetChildForCondition(DataModelConditionPredicate, DisplayConditionSide.Right);
RightSideDataModel.ApplyTypeFilter(true, leftSideType);
}
else
RightStaticValue = DataModelConditionPredicate.RightStaticValue;
}
public void ApplyLeftSide()
{
DataModelConditionPredicate.UpdateLeftSide(SelectedLeftSideProperty.DataModel, SelectedLeftSideProperty.PropertyPath);
_profileEditorService.UpdateSelectedProfileElement();
SelectedOperator = DataModelConditionPredicate.Operator;
Update();
}
public void ApplyRightSideDynamic()
{
DataModelConditionPredicate.UpdateRightSide(SelectedRightSideProperty.DataModel, SelectedRightSideProperty.PropertyPath);
_profileEditorService.UpdateSelectedProfileElement();
Update();
}
public void ApplyRightSideStatic(object value, bool isSubmitted)
{
if (isSubmitted)
{
DataModelConditionPredicate.UpdateRightSide(value);
_profileEditorService.UpdateSelectedProfileElement();
Update();
}
RightSideTransitionIndex = 0;
RightSideInputViewModel = null;
RightStaticValue = value;
_eventAggregator.Unsubscribe(this);
}
public void ApplyOperator()
{
DataModelConditionPredicate.UpdateOperator(SelectedOperator);
_profileEditorService.UpdateSelectedProfileElement();
Update();
}
public void ActivateRightSideInputViewModel()
{
if (SelectedLeftSideProperty?.PropertyInfo == null)
return;
RightSideTransitionIndex = 1;
RightSideInputViewModel = _dataModelUIService.GetDataModelInputViewModel(
SelectedLeftSideProperty.PropertyInfo.PropertyType,
SelectedLeftSideProperty.PropertyDescription,
DataModelConditionPredicate.RightStaticValue,
ApplyRightSideStatic
);
_eventAggregator.Subscribe(this);
}
private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
{
if (LeftSideDataModelOpen)
LeftSideDataModel.Update(_dataModelUIService);
else if (RightSideDataModelOpen)
RightSideDataModel.Update(_dataModelUIService);
}
private void RightDataModelUpdateRequested(object sender, EventArgs e)
{
var leftSideType = SelectedLeftSideProperty?.PropertyInfo?.PropertyType;
if (DataModelConditionPredicate.PredicateType == ProfileRightSideType.Dynamic)
SelectedRightSideProperty = LeftSideDataModel.GetChildForCondition(DataModelConditionPredicate, DisplayConditionSide.Right);
// With the data model updated, also reapply the filter
RightSideDataModel.ApplyTypeFilter(true, leftSideType);
}
private void LeftDataModelUpdateRequested(object sender, EventArgs e)
{
if (DataModelConditionPredicate.PredicateType == ProfileRightSideType.Static)
LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray());
}
private void ExecuteSelectLeftProperty(object context)
{
if (!(context is DataModelVisualizationViewModel dataModelVisualizationViewModel))
return;
SelectedLeftSideProperty = dataModelVisualizationViewModel;
ApplyLeftSide();
}
private void ExecuteSelectRightProperty(object context)
{
if (!(context is DataModelVisualizationViewModel dataModelVisualizationViewModel))
return;
SelectedRightSideProperty = dataModelVisualizationViewModel;
ApplyRightSideDynamic();
}
private void ExecuteSelectOperatorCommand(object context)
{
if (!(context is ConditionOperator displayConditionOperator))
return;
SelectedOperator = displayConditionOperator;
ApplyOperator();
}
public void Dispose()
{
_updateTimer.Dispose();
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
}
}
}

View File

@ -1,23 +1,24 @@
using System.Linq; using System.Linq;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Ninject.Factories; using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.Conditions;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Stylet; using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
{ {
public class DisplayConditionsViewModel : Conductor<DisplayConditionGroupViewModel>, IProfileEditorPanelViewModel public class DisplayConditionsViewModel : Conductor<DataModelConditionGroupViewModel>, IProfileEditorPanelViewModel
{ {
private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory; private readonly IDataModelConditionsVmFactory _dataModelConditionsVmFactory;
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private RenderProfileElement _renderProfileElement; private RenderProfileElement _renderProfileElement;
private bool _displayStartHint; private bool _displayStartHint;
public DisplayConditionsViewModel(IProfileEditorService profileEditorService, IDisplayConditionsVmFactory displayConditionsVmFactory) public DisplayConditionsViewModel(IProfileEditorService profileEditorService, IDataModelConditionsVmFactory dataModelConditionsVmFactory)
{ {
_profileEditorService = profileEditorService; _profileEditorService = profileEditorService;
_displayConditionsVmFactory = displayConditionsVmFactory; _dataModelConditionsVmFactory = dataModelConditionsVmFactory;
} }
public bool DisplayStartHint public bool DisplayStartHint
@ -82,10 +83,10 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
} }
// Ensure the layer has a root display condition group // Ensure the layer has a root display condition group
if (e.RenderProfileElement.DataModelConditionGroup == null) if (e.RenderProfileElement.DisplayCondition == null)
e.RenderProfileElement.DataModelConditionGroup = new DataModelConditionGroup(null); e.RenderProfileElement.DisplayCondition = new DataModelConditionGroup(null);
ActiveItem = _displayConditionsVmFactory.DisplayConditionGroupViewModel(e.RenderProfileElement.DataModelConditionGroup, false); ActiveItem = _dataModelConditionsVmFactory.DataModelConditionGroupViewModel(e.RenderProfileElement.DisplayCondition, false);
ActiveItem.IsRootGroup = true; ActiveItem.IsRootGroup = true;
ActiveItem.Update(); ActiveItem.Update();

View File

@ -11,7 +11,7 @@
<UserControl.Resources> <UserControl.Resources>
<ResourceDictionary> <ResourceDictionary>
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DisplayConditions.xaml" /> <ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DataModelConditions.xaml" />
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
</ResourceDictionary> </ResourceDictionary>
</UserControl.Resources> </UserControl.Resources>

View File

@ -14,7 +14,7 @@
<UserControl.Resources> <UserControl.Resources>
<ResourceDictionary> <ResourceDictionary>
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DisplayConditions.xaml" /> <ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DataModelConditions.xaml" />
<ResourceDictionary> <ResourceDictionary>
<converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" /> <converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<utilities:BindingProxy x:Key="DataContextProxy" Data="{Binding}" /> <utilities:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
@ -51,7 +51,7 @@
</Button> </Button>
<Button Grid.Column="2" <Button Grid.Column="2"
Style="{StaticResource DisplayConditionButtonLeftClickMenu}" Style="{StaticResource DataModelConditionButtonLeftClickMenu}"
Background="#7B7B7B" Background="#7B7B7B"
BorderBrush="#7B7B7B" BorderBrush="#7B7B7B"
Click="PropertyButton_OnClick"> Click="PropertyButton_OnClick">

View File

@ -114,7 +114,7 @@
<!-- Conditions resize --> <!-- Conditions resize -->
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<!-- Display conditions --> <!-- Display conditions -->
<RowDefinition Height="{Binding DisplayConditionsHeight.Value, Mode=TwoWay}" MinHeight="100" /> <RowDefinition Height="{Binding DataModelConditionsHeight.Value, Mode=TwoWay}" MinHeight="100" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<!-- Profile selection --> <!-- Profile selection -->

View File

@ -25,7 +25,7 @@ namespace Artemis.UI.Screens.ProfileEditor
private readonly ISettingsService _settingsService; private readonly ISettingsService _settingsService;
private readonly ISnackbarMessageQueue _snackbarMessageQueue; private readonly ISnackbarMessageQueue _snackbarMessageQueue;
private PluginSetting<GridLength> _bottomPanelsHeight; private PluginSetting<GridLength> _bottomPanelsHeight;
private PluginSetting<GridLength> _displayConditionsHeight; private PluginSetting<GridLength> _dataModelConditionsHeight;
private DisplayConditionsViewModel _displayConditionsViewModel; private DisplayConditionsViewModel _displayConditionsViewModel;
private PluginSetting<GridLength> _elementPropertiesWidth; private PluginSetting<GridLength> _elementPropertiesWidth;
private LayerPropertiesViewModel _layerPropertiesViewModel; private LayerPropertiesViewModel _layerPropertiesViewModel;
@ -38,7 +38,7 @@ namespace Artemis.UI.Screens.ProfileEditor
public ProfileEditorViewModel(ProfileModule module, public ProfileEditorViewModel(ProfileModule module,
ProfileViewModel profileViewModel, ProfileViewModel profileViewModel,
ProfileTreeViewModel profileTreeViewModel, ProfileTreeViewModel profileTreeViewModel,
DisplayConditionsViewModel displayConditionsViewModel, DisplayConditionsViewModel dataModelConditionsViewModel,
LayerPropertiesViewModel layerPropertiesViewModel, LayerPropertiesViewModel layerPropertiesViewModel,
IProfileEditorService profileEditorService, IProfileEditorService profileEditorService,
IProfileService profileService, IProfileService profileService,
@ -62,12 +62,12 @@ namespace Artemis.UI.Screens.ProfileEditor
// Populate the panels // Populate the panels
ProfileViewModel = profileViewModel; ProfileViewModel = profileViewModel;
ProfileTreeViewModel = profileTreeViewModel; ProfileTreeViewModel = profileTreeViewModel;
DisplayConditionsViewModel = displayConditionsViewModel; DisplayConditionsViewModel = dataModelConditionsViewModel;
LayerPropertiesViewModel = layerPropertiesViewModel; LayerPropertiesViewModel = layerPropertiesViewModel;
Items.Add(ProfileViewModel); Items.Add(ProfileViewModel);
Items.Add(ProfileTreeViewModel); Items.Add(ProfileTreeViewModel);
Items.Add(DisplayConditionsViewModel); Items.Add(dataModelConditionsViewModel);
Items.Add(LayerPropertiesViewModel); Items.Add(LayerPropertiesViewModel);
} }
@ -110,10 +110,10 @@ namespace Artemis.UI.Screens.ProfileEditor
set => SetAndNotify(ref _sidePanelsWidth, value); set => SetAndNotify(ref _sidePanelsWidth, value);
} }
public PluginSetting<GridLength> DisplayConditionsHeight public PluginSetting<GridLength> DataModelConditionsHeight
{ {
get => _displayConditionsHeight; get => _dataModelConditionsHeight;
set => SetAndNotify(ref _displayConditionsHeight, value); set => SetAndNotify(ref _dataModelConditionsHeight, value);
} }
public PluginSetting<GridLength> BottomPanelsHeight public PluginSetting<GridLength> BottomPanelsHeight
@ -303,7 +303,7 @@ namespace Artemis.UI.Screens.ProfileEditor
private void LoadWorkspaceSettings() private void LoadWorkspaceSettings()
{ {
SidePanelsWidth = _settingsService.GetSetting("ProfileEditor.SidePanelsWidth", new GridLength(385)); SidePanelsWidth = _settingsService.GetSetting("ProfileEditor.SidePanelsWidth", new GridLength(385));
DisplayConditionsHeight = _settingsService.GetSetting("ProfileEditor.DisplayConditionsHeight", new GridLength(345)); DataModelConditionsHeight = _settingsService.GetSetting("ProfileEditor.DataModelConditionsHeight", new GridLength(345));
BottomPanelsHeight = _settingsService.GetSetting("ProfileEditor.BottomPanelsHeight", new GridLength(265)); BottomPanelsHeight = _settingsService.GetSetting("ProfileEditor.BottomPanelsHeight", new GridLength(265));
ElementPropertiesWidth = _settingsService.GetSetting("ProfileEditor.ElementPropertiesWidth", new GridLength(545)); ElementPropertiesWidth = _settingsService.GetSetting("ProfileEditor.ElementPropertiesWidth", new GridLength(545));
} }
@ -311,7 +311,7 @@ namespace Artemis.UI.Screens.ProfileEditor
private void SaveWorkspaceSettings() private void SaveWorkspaceSettings()
{ {
SidePanelsWidth.Save(); SidePanelsWidth.Save();
DisplayConditionsHeight.Save(); DataModelConditionsHeight.Save();
BottomPanelsHeight.Save(); BottomPanelsHeight.Save();
ElementPropertiesWidth.Save(); ElementPropertiesWidth.Save();
} }