Merge branch 'feature/visual-scripting' into feature/avalonia

This commit is contained in:
Robert committed 2021-11-17 23:07:36 +01:00
commit 4ef7c83c74
366 files changed
+11986 -10866

No files matched your search

@@ -1,337 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.Core.Services;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Input;
using Artemis.UI.Shared.Services;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.Conditions.Abstract
{
public abstract class DataModelConditionPredicateViewModel : DataModelConditionViewModel, IDisposable
{
private readonly IConditionOperatorService _conditionOperatorService;
private readonly IDataModelUIService _dataModelUIService;
private readonly List<Module> _modules;
private readonly IProfileEditorService _profileEditorService;
private DataModelStaticViewModel _rightSideInputViewModel;
private DataModelDynamicViewModel _rightSideSelectionViewModel;
private BaseConditionOperator _selectedOperator;
private List<Type> _supportedInputTypes;
protected DataModelConditionPredicateViewModel(
DataModelConditionPredicate dataModelConditionPredicate,
List<Module> modules,
IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService,
IConditionOperatorService conditionOperatorService,
ISettingsService settingsService) : base(dataModelConditionPredicate)
{
_modules = modules;
_profileEditorService = profileEditorService;
_dataModelUIService = dataModelUIService;
_conditionOperatorService = conditionOperatorService;
_supportedInputTypes = new List<Type>();
SelectOperatorCommand = new DelegateCommand(ExecuteSelectOperatorCommand);
Operators = new BindableCollection<BaseConditionOperator>();
ShowDataModelValues = settingsService.GetSetting<bool>("ProfileEditor.ShowDataModelValues");
}
public DataModelConditionPredicate DataModelConditionPredicate => (DataModelConditionPredicate) Model;
public PluginSetting<bool> ShowDataModelValues { get; }
public bool CanSelectOperator => DataModelConditionPredicate.LeftPath is {IsValid: true};
public BaseConditionOperator 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 BindableCollection<BaseConditionOperator> Operators { get; }
protected SolidColorBrush LeftSideColor { get; set; }
public override void Evaluate()
{
IsConditionMet = DataModelConditionPredicate.Evaluate();
}
public override void Delete()
{
base.Delete();
_profileEditorService.SaveSelectedProfileElement();
}
public virtual void Initialize()
{
LeftSideSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_modules);
LeftSideSelectionViewModel.PropertySelected += LeftSideOnPropertySelected;
if (LeftSideColor != null)
LeftSideSelectionViewModel.ButtonBrush = LeftSideColor;
// Determine which types are currently supported
_supportedInputTypes = GetSupportedInputTypes();
Update();
}
public override void Update()
{
LeftSideSelectionViewModel.FilterTypes = _supportedInputTypes.ToArray();
LeftSideSelectionViewModel.ChangeDataModelPath(DataModelConditionPredicate.LeftPath);
Type leftSideType = GetLeftSideType();
// Get the supported operators
Operators.Clear();
Operators.AddRange(_conditionOperatorService.GetConditionOperatorsForType(leftSideType ?? typeof(object), ConditionParameterSide.Left));
if (DataModelConditionPredicate.Operator == null)
DataModelConditionPredicate.UpdateOperator(Operators.FirstOrDefault());
// The core doesn't care about best matches so if there is a new preferred operator, use that instead
else if (!Operators.Contains(DataModelConditionPredicate.Operator))
DataModelConditionPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.Description == DataModelConditionPredicate.Operator.Description) ?? Operators.FirstOrDefault());
NotifyOfPropertyChange(nameof(CanSelectOperator));
SelectedOperator = DataModelConditionPredicate.Operator;
// Without a selected operator or one that supports a right side, leave the right side input empty
if (SelectedOperator?.RightSideType == null)
{
DisposeRightSideStaticViewModel();
DisposeRightSideDynamicViewModel();
return;
}
// Ensure the right side has the proper VM
if (DataModelConditionPredicate.PredicateType == ProfileRightSideType.Dynamic)
{
DisposeRightSideStaticViewModel();
if (RightSideSelectionViewModel == null)
CreateRightSideSelectionViewModel();
RightSideSelectionViewModel.ChangeDataModelPath(DataModelConditionPredicate.RightPath);
RightSideSelectionViewModel.FilterTypes = new[] {SelectedOperator.RightSideType};
}
else
{
DisposeRightSideDynamicViewModel();
if (RightSideInputViewModel == null)
CreateRightSideInputViewModel();
Type preferredType = DataModelConditionPredicate.GetPreferredRightSideType();
if (preferredType != null && RightSideInputViewModel.TargetType != preferredType)
RightSideInputViewModel.UpdateTargetType(preferredType);
RightSideInputViewModel.Value = DataModelConditionPredicate.RightStaticValue;
}
}
public void ApplyLeftSide()
{
Type newType = LeftSideSelectionViewModel.DataModelPath.GetPropertyType();
bool converted = ConvertIfRequired(newType);
if (converted)
return;
DataModelConditionPredicate.UpdateLeftSide(LeftSideSelectionViewModel.DataModelPath);
_profileEditorService.SaveSelectedProfileElement();
SelectedOperator = DataModelConditionPredicate.Operator;
Update();
}
public void ApplyRightSideDynamic()
{
DataModelConditionPredicate.UpdateRightSideDynamic(RightSideSelectionViewModel.DataModelPath);
_profileEditorService.SaveSelectedProfileElement();
Update();
}
public void ApplyRightSideStatic(object value)
{
DataModelConditionPredicate.UpdateRightSideStatic(value);
_profileEditorService.SaveSelectedProfileElement();
Update();
}
public void ApplyOperator()
{
DataModelConditionPredicate.UpdateOperator(SelectedOperator);
_profileEditorService.SaveSelectedProfileElement();
Update();
}
protected abstract List<Type> GetSupportedInputTypes();
protected abstract Type GetLeftSideType();
protected virtual List<DataModelPropertiesViewModel> GetExtraRightSideDataModelViewModels()
{
return null;
}
private void ExecuteSelectOperatorCommand(object context)
{
if (!(context is BaseConditionOperator dataModelConditionOperator))
return;
SelectedOperator = dataModelConditionOperator;
ApplyOperator();
}
public override void UpdateModules()
{
if (LeftSideSelectionViewModel != null)
{
LeftSideSelectionViewModel.PropertySelected -= LeftSideOnPropertySelected;
LeftSideSelectionViewModel.Dispose();
LeftSideSelectionViewModel = null;
}
DisposeRightSideStaticViewModel();
DisposeRightSideDynamicViewModel();
// If the modules changed the paths may no longer be valid if they targeted a module no longer available, in that case clear the path
if (DataModelConditionPredicate.LeftPath?.Target != null && !DataModelConditionPredicate.LeftPath.Target.IsExpansion && !_modules.Contains(DataModelConditionPredicate.LeftPath.Target.Module))
DataModelConditionPredicate.UpdateLeftSide(null);
if (DataModelConditionPredicate.RightPath?.Target != null && !DataModelConditionPredicate.RightPath.Target.IsExpansion && !_modules.Contains(DataModelConditionPredicate.RightPath.Target.Module))
DataModelConditionPredicate.UpdateRightSideDynamic(null);
Initialize();
}
#region IDisposable
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (LeftSideSelectionViewModel != null)
{
LeftSideSelectionViewModel.PropertySelected -= LeftSideOnPropertySelected;
LeftSideSelectionViewModel.Dispose();
LeftSideSelectionViewModel = null;
}
DisposeRightSideStaticViewModel();
DisposeRightSideDynamicViewModel();
}
}
/// <inheritdoc />
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
#region View model creation
private void CreateRightSideSelectionViewModel()
{
RightSideSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_modules);
RightSideSelectionViewModel.ButtonBrush = (SolidColorBrush) Application.Current.FindResource("PrimaryHueMidBrush");
RightSideSelectionViewModel.DisplaySwitchButton = true;
RightSideSelectionViewModel.PropertySelected += RightSideOnPropertySelected;
RightSideSelectionViewModel.SwitchToStaticRequested += RightSideSelectionViewModelOnSwitchToStaticRequested;
List<DataModelPropertiesViewModel> extra = GetExtraRightSideDataModelViewModels();
if (extra != null)
RightSideSelectionViewModel.ExtraDataModelViewModels.AddRange(extra);
}
private void CreateRightSideInputViewModel()
{
Type preferredType = DataModelConditionPredicate.GetPreferredRightSideType();
RightSideInputViewModel = _dataModelUIService.GetStaticInputViewModel(preferredType, LeftSideSelectionViewModel.DataModelPath?.GetPropertyDescription());
RightSideInputViewModel.ButtonBrush = (SolidColorBrush) Application.Current.FindResource("PrimaryHueMidBrush");
RightSideInputViewModel.DisplaySwitchButton = true;
RightSideInputViewModel.ValueUpdated += RightSideOnValueEntered;
RightSideInputViewModel.SwitchToDynamicRequested += RightSideInputViewModelOnSwitchToDynamicRequested;
}
private void DisposeRightSideStaticViewModel()
{
if (RightSideInputViewModel == null)
return;
RightSideInputViewModel.ValueUpdated -= RightSideOnValueEntered;
RightSideInputViewModel.SwitchToDynamicRequested -= RightSideInputViewModelOnSwitchToDynamicRequested;
RightSideInputViewModel.Dispose();
RightSideInputViewModel = null;
}
private void DisposeRightSideDynamicViewModel()
{
if (RightSideSelectionViewModel == null)
return;
RightSideSelectionViewModel.PropertySelected -= RightSideOnPropertySelected;
RightSideSelectionViewModel.SwitchToStaticRequested -= RightSideSelectionViewModelOnSwitchToStaticRequested;
RightSideSelectionViewModel.Dispose();
RightSideSelectionViewModel = null;
}
#endregion
#region Event handlers
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 RightSideSelectionViewModelOnSwitchToStaticRequested(object sender, EventArgs e)
{
DataModelConditionPredicate.PredicateType = ProfileRightSideType.Static;
// Ensure the right static value is never null when the preferred type is a value type
Type preferredType = DataModelConditionPredicate.GetPreferredRightSideType();
if (DataModelConditionPredicate.RightStaticValue == null && preferredType != null && preferredType.IsValueType)
DataModelConditionPredicate.UpdateRightSideStatic(preferredType.GetDefault());
Update();
}
private void RightSideInputViewModelOnSwitchToDynamicRequested(object sender, EventArgs e)
{
DataModelConditionPredicate.PredicateType = ProfileRightSideType.Dynamic;
Update();
}
#endregion
}
}
@@ -1,69 +0,0 @@
using System;
using Artemis.Core;
using Artemis.UI.Shared.Input;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.Conditions.Abstract
{
public abstract class DataModelConditionViewModel : Conductor<DataModelConditionViewModel>.Collection.AllActive
{
private DataModelDynamicViewModel _leftSideSelectionViewModel;
private bool _isConditionMet;
protected DataModelConditionViewModel(DataModelConditionPart model)
{
Model = model;
}
public DataModelConditionPart Model { get; }
public DataModelDynamicViewModel LeftSideSelectionViewModel
{
get => _leftSideSelectionViewModel;
set => SetAndNotify(ref _leftSideSelectionViewModel, value);
}
public bool IsConditionMet
{
get => _isConditionMet;
set => SetAndNotify(ref _isConditionMet, value);
}
public abstract void Update();
public abstract void Evaluate();
public virtual void Delete()
{
Model.Parent.RemoveChild(Model);
((DataModelConditionViewModel) Parent).Update();
}
protected bool ConvertIfRequired(Type newType)
{
if (newType == null)
return false;
if (!(Parent is DataModelConditionGroupViewModel groupViewModel))
return false;
// List
if (newType.IsGenericEnumerable())
{
if (this is DataModelConditionListViewModel)
return false;
groupViewModel.ConvertToConditionList(this);
return true;
}
// Predicate
if (this is DataModelConditionPredicateViewModel)
return false;
groupViewModel.ConvertToPredicate(this);
return true;
}
public abstract void UpdateModules();
}
}
@@ -1,101 +0,0 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.Conditions.DataModelConditionEventView"
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.Conditions"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DataModelConditions.xaml" />
</ResourceDictionary.MergedDictionaries>
<Storyboard x:Key="EventAnimation" x:Name="StartAnimation">
<DoubleAnimation Duration="0:0:0.5" From="0.5" To="1.5" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)" />
<DoubleAnimation Duration="0:0:0.5" From="0.5" To="1.5" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)" />
<DoubleAnimation Duration="0:0:0.1" From="0" To="1" Storyboard.TargetProperty="(UIElement.Opacity)" />
<DoubleAnimation BeginTime="0:0:0.25" Duration="0:0:0.25" From="1" To="0" Storyboard.TargetProperty="(UIElement.Opacity)" />
</Storyboard>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Row="0"
Grid.Column="0"
ToolTip="Delete the event trigger"
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 LeftSideSelectionViewModel}"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsTabStop="False" />
<TextBlock Grid.Row="0"
Grid.Column="2"
VerticalAlignment="Center"
Margin="5 0 0 0" Text="{Binding TriggerPastParticiple}">
</TextBlock>
<Border Grid.Row="0"
Grid.Column="3"
Opacity="0"
Background="#FFB9A40A"
Width="22"
Height="22"
CornerRadius="11"
HorizontalAlignment="Left"
Margin="5 0 0 0"
ToolTip="{Binding LastTrigger, NotifyOnTargetUpdated=True}"
ToolTipService.IsEnabled="False">
<Border.RenderTransform>
<ScaleTransform ScaleX="1" ScaleY="1" CenterX="11" CenterY="11" />
</Border.RenderTransform>
<Border.Triggers>
<!-- Ugly workaround to avoid the event animation showing on load -->
<EventTrigger RoutedEvent="Border.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation BeginTime="0:0:0.01" Duration="0:0:0" From="1" To="0" Storyboard.TargetProperty="(UIElement.Opacity)" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard Storyboard="{StaticResource EventAnimation}" />
</EventTrigger>
</Border.Triggers>
<materialDesign:PackIcon Kind="LightningBolt" VerticalAlignment="Center" HorizontalAlignment="Center" Width="16" Height="16" />
</Border>
<ItemsControl Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3" ItemsSource="{Binding Items}" Margin="0 3 0 0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
@@ -1,147 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Media;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
namespace Artemis.UI.Screens.ProfileEditor.Conditions
{
public sealed class DataModelConditionEventViewModel : DataModelConditionViewModel, IDisposable
{
private readonly IDataModelConditionsVmFactory _dataModelConditionsVmFactory;
private readonly IDataModelUIService _dataModelUIService;
private readonly List<Module> _modules;
private readonly IProfileEditorService _profileEditorService;
private DateTime _lastTrigger;
private string _triggerPastParticiple;
public DataModelConditionEventViewModel(DataModelConditionEvent dataModelConditionEvent,
List<Module> modules,
IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService,
IDataModelConditionsVmFactory dataModelConditionsVmFactory) : base(dataModelConditionEvent)
{
_modules = modules;
_profileEditorService = profileEditorService;
_dataModelUIService = dataModelUIService;
_dataModelConditionsVmFactory = dataModelConditionsVmFactory;
_lastTrigger = DataModelConditionEvent.LastTrigger;
}
public DataModelConditionEvent DataModelConditionEvent => (DataModelConditionEvent) Model;
public DateTime LastTrigger
{
get => _lastTrigger;
set => SetAndNotify(ref _lastTrigger, value);
}
public string TriggerPastParticiple
{
get => _triggerPastParticiple;
set => SetAndNotify(ref _triggerPastParticiple, value);
}
public void Initialize()
{
LeftSideSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_modules);
LeftSideSelectionViewModel.PropertySelected += LeftSideSelectionViewModelOnPropertySelected;
LeftSideSelectionViewModel.LoadEventChildren = false;
IReadOnlyCollection<DataModelVisualizationRegistration> editors = _dataModelUIService.RegisteredDataModelEditors;
List<Type> supportedInputTypes = editors.Select(e => e.SupportedType).ToList();
supportedInputTypes.AddRange(editors.Where(e => e.CompatibleConversionTypes != null).SelectMany(e => e.CompatibleConversionTypes));
supportedInputTypes.Add(typeof(DataModelEvent));
supportedInputTypes.Add(typeof(DataModelEvent<>));
LeftSideSelectionViewModel.FilterTypes = supportedInputTypes.ToArray();
LeftSideSelectionViewModel.ButtonBrush = new SolidColorBrush(Color.FromRgb(185, 164, 10));
LeftSideSelectionViewModel.Placeholder = "Select an event";
Update();
}
public override void Update()
{
LeftSideSelectionViewModel.ChangeDataModelPath(DataModelConditionEvent.EventPath);
// Remove VMs of effects no longer applied on the layer
Items.RemoveRange(Items.Where(c => !DataModelConditionEvent.Children.Contains(c.Model)).ToList());
if (DataModelConditionEvent.EventPath == null || !DataModelConditionEvent.EventPath.IsValid)
return;
TriggerPastParticiple = DataModelConditionEvent.GetDataModelEvent()?.TriggerPastParticiple;
List<DataModelConditionViewModel> viewModels = new();
foreach (DataModelConditionPart childModel in Model.Children)
{
if (Items.Any(c => c.Model == childModel))
continue;
if (!(childModel is DataModelConditionGroup dataModelConditionGroup))
continue;
DataModelConditionGroupViewModel viewModel = _dataModelConditionsVmFactory.DataModelConditionGroupViewModel(dataModelConditionGroup, ConditionGroupType.Event, _modules);
viewModel.IsRootGroup = true;
viewModels.Add(viewModel);
}
if (viewModels.Any())
Items.AddRange(viewModels);
foreach (DataModelConditionViewModel childViewModel in Items)
childViewModel.Update();
}
public override void Evaluate()
{
LastTrigger = DataModelConditionEvent.LastTrigger;
IsConditionMet = DataModelConditionEvent.Evaluate();
}
public void ApplyEvent()
{
DataModelConditionEvent.UpdateEvent(LeftSideSelectionViewModel.DataModelPath);
_profileEditorService.SaveSelectedProfileElement();
Update();
}
public override void UpdateModules()
{
LeftSideSelectionViewModel.Dispose();
LeftSideSelectionViewModel.PropertySelected -= LeftSideSelectionViewModelOnPropertySelected;
Initialize();
}
protected override void OnInitialActivate()
{
Initialize();
base.OnInitialActivate();
}
#region Event handlers
private void LeftSideSelectionViewModelOnPropertySelected(object sender, DataModelInputDynamicEventArgs e)
{
ApplyEvent();
}
#endregion
#region IDisposable
public void Dispose()
{
LeftSideSelectionViewModel.Dispose();
LeftSideSelectionViewModel.PropertySelected -= LeftSideSelectionViewModelOnPropertySelected;
}
#endregion
}
}
@@ -1,165 +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:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:Converters="clr-namespace:Artemis.UI.Converters"
xmlns:utilities="clr-namespace:Artemis.UI.Utilities"
xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.Conditions"
x:Class="Artemis.UI.Screens.ProfileEditor.Conditions.DataModelConditionGroupView"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance IsDesignTimeCreatable=False, Type={x:Type local:DataModelConditionGroupViewModel}}">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DataModelConditions.xaml" />
</ResourceDictionary.MergedDictionaries>
<Converters:InverseBooleanConverter x:Key="InverseBooleanConverter" />
<utilities:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Row="0"
Grid.Column="0"
ToolTip="Delete the group and all its children"
Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Left"
Visibility="{Binding IsRootGroup, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}"
Width="25"
Height="25"
Foreground="#E74C4C"
Command="{s:Action Delete}">
<materialDesign:PackIcon Kind="Close" Width="18" Height="18" />
</Button>
<Button Grid.Row="0"
Grid.Column="1"
Style="{StaticResource DataModelConditionButtonLeftClickMenu}"
Background="#E74C4C"
BorderBrush="#E74C4C"
Margin="3 1"
Content="{Binding SelectedBooleanOperator}"
Visibility="{Binding DisplayBooleanOperator, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}"
IsEnabled="{Binding IsEventGroup, Converter={StaticResource InverseBooleanConverter}}"
ToolTipService.ShowOnDisabled="True">
<Button.ToolTip>
<StackPanel>
<TextBlock Visibility="{Binding IsEventGroup, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}">
Change the operator of the group, determining which conditions should match
</TextBlock>
<TextBlock Visibility="{Binding IsEventGroup, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
The operator of the root group cannot be changed for event-based conditions
</TextBlock>
</StackPanel>
</Button.ToolTip>
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="And"
Command="{s:Action SelectBooleanOperator}"
CommandParameter="And"
ToolTip="All the conditions in the group should evaluate to true" />
<MenuItem Header="Or"
Command="{s:Action SelectBooleanOperator}"
CommandParameter="Or"
ToolTip="Any of the conditions in the group should evaluate to true" />
<MenuItem Header="And not"
Command="{s:Action SelectBooleanOperator}"
CommandParameter="AndNot"
ToolTip="All the conditions in the group should evaluate to false" />
<MenuItem Header="Or not"
Command="{s:Action SelectBooleanOperator}"
CommandParameter="OrNot"
ToolTip="Any of the conditions in the group should evaluate to false" />
</ContextMenu>
</Button.ContextMenu>
</Button>
<Button x:Name="AddChildButton"
Grid.Row="0"
Grid.Column="2"
ToolTip="Add another condition to the current group"
HorizontalAlignment="Left"
Foreground="#4CE758"
Width="25"
Height="25">
<Button.Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource MaterialDesignIconForegroundButton}">
<Style.Triggers>
<EventTrigger RoutedEvent="ButtonBase.Click">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="ContextMenu.IsOpen">
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Add condition" ToolTip="A condition that evaluates the state of a property in the data model" Command="{s:Action AddCondition}">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="Equal" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Add event condition"
ToolTip="An event condition that responds to data model events"
Command="{s:Action AddEventCondition}"
Visibility="{Binding Data.CanAddEventCondition, Source={StaticResource DataContextProxy}, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="LightningBolt" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Add group" ToolTip="A group for conditions and other groups" Command="{s:Action AddGroup}">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="CodeParentheses" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</Button.ContextMenu>
<materialDesign:PackIcon Kind="Add" Width="18" Height="18" />
</Button>
<ToggleButton Grid.Row="0"
Grid.Column="3"
Style="{StaticResource MaterialDesignActionToggleButton}"
Focusable="False"
IsHitTestVisible="False"
IsChecked="{Binding IsConditionMet}"
Visibility="{Binding DisplayEvaluationResult, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}"
Width="22"
Height="22"
HorizontalAlignment="Left">
<ToggleButton.Content>
<Border Background="#E74C4C" Width="22" Height="22">
<materialDesign:PackIcon Kind="Close" VerticalAlignment="Center" HorizontalAlignment="Center" Width="16" Height="16"/>
</Border>
</ToggleButton.Content>
<materialDesign:ToggleButtonAssist.OnContent>
<materialDesign:PackIcon Kind="Check" Width="16" Height="16"/>
</materialDesign:ToggleButtonAssist.OnContent>
</ToggleButton>
<ItemsControl Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3" ItemsSource="{Binding Items}" Margin="0 3 0 0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
@@ -1,259 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.Core.Services;
using Artemis.UI.Extensions;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
using Artemis.UI.Shared.Services;
using Humanizer;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.Conditions
{
public class DataModelConditionGroupViewModel : DataModelConditionViewModel
{
private readonly IDataModelConditionsVmFactory _dataModelConditionsVmFactory;
private readonly List<Module> _modules;
private readonly ICoreService _coreService;
private readonly IProfileEditorService _profileEditorService;
private bool _isEventGroup;
private bool _isRootGroup;
public DataModelConditionGroupViewModel(DataModelConditionGroup dataModelConditionGroup,
ConditionGroupType groupType,
List<Module> modules,
ICoreService coreService,
IProfileEditorService profileEditorService,
IDataModelConditionsVmFactory dataModelConditionsVmFactory)
: base(dataModelConditionGroup)
{
GroupType = groupType;
_modules = modules;
_coreService = coreService;
_profileEditorService = profileEditorService;
_dataModelConditionsVmFactory = dataModelConditionsVmFactory;
Items.CollectionChanged += (_, _) => NotifyOfPropertyChange(nameof(DisplayBooleanOperator));
}
public ConditionGroupType GroupType { get; }
public DataModelConditionGroup DataModelConditionGroup => (DataModelConditionGroup) Model;
public bool IsRootGroup
{
get => _isRootGroup;
set
{
if (!SetAndNotify(ref _isRootGroup, value)) return;
NotifyOfPropertyChange(nameof(CanAddEventCondition));
}
}
public bool CanAddEventCondition => IsRootGroup && GroupType == ConditionGroupType.General;
public bool IsEventGroup
{
get => _isEventGroup;
set
{
SetAndNotify(ref _isEventGroup, value);
NotifyOfPropertyChange(nameof(DisplayEvaluationResult));
}
}
public bool DisplayBooleanOperator => Items.Count > 1;
public bool DisplayEvaluationResult => GroupType == ConditionGroupType.General && !IsEventGroup;
public string SelectedBooleanOperator => DataModelConditionGroup.BooleanOperator.Humanize();
public void SelectBooleanOperator(string type)
{
BooleanOperator enumValue = Enum.Parse<BooleanOperator>(type);
DataModelConditionGroup.BooleanOperator = enumValue;
NotifyOfPropertyChange(nameof(SelectedBooleanOperator));
_profileEditorService.SaveSelectedProfileElement();
}
public void AddCondition()
{
switch (GroupType)
{
case ConditionGroupType.General:
DataModelConditionGroup.AddChild(new DataModelConditionGeneralPredicate(DataModelConditionGroup, ProfileRightSideType.Dynamic));
break;
case ConditionGroupType.List:
DataModelConditionGroup.AddChild(new DataModelConditionListPredicate(DataModelConditionGroup, ProfileRightSideType.Dynamic));
break;
case ConditionGroupType.Event:
DataModelConditionGroup.AddChild(new DataModelConditionEventPredicate(DataModelConditionGroup, ProfileRightSideType.Dynamic));
break;
default:
throw new ArgumentOutOfRangeException();
}
Update();
_profileEditorService.SaveSelectedProfileElement();
}
public void AddEventCondition()
{
if (!CanAddEventCondition)
return;
// Find a good spot for the event, behind the last existing event
int index = 0;
DataModelConditionPart existing = DataModelConditionGroup.Children.LastOrDefault(c => c is DataModelConditionEvent);
if (existing != null)
index = DataModelConditionGroup.Children.IndexOf(existing) + 1;
DataModelConditionGroup.AddChild(new DataModelConditionEvent(DataModelConditionGroup), index);
Update();
_profileEditorService.SaveSelectedProfileElement();
}
public void AddGroup()
{
DataModelConditionGroup.AddChild(new DataModelConditionGroup(DataModelConditionGroup));
Update();
_profileEditorService.SaveSelectedProfileElement();
}
public override void Update()
{
NotifyOfPropertyChange(nameof(SelectedBooleanOperator));
// Remove VMs of effects no longer applied on the layer
List<DataModelConditionViewModel> toRemove = Items.Where(c => !DataModelConditionGroup.Children.Contains(c.Model)).ToList();
if (toRemove.Any())
Items.RemoveRange(toRemove);
foreach (DataModelConditionPart childModel in Model.Children)
{
if (Items.Any(c => c.Model == childModel))
continue;
switch (childModel)
{
case DataModelConditionGroup dataModelConditionGroup:
Items.Add(_dataModelConditionsVmFactory.DataModelConditionGroupViewModel(dataModelConditionGroup, GroupType, _modules));
break;
case DataModelConditionList dataModelConditionList:
Items.Add(_dataModelConditionsVmFactory.DataModelConditionListViewModel(dataModelConditionList, _modules));
break;
case DataModelConditionEvent dataModelConditionEvent:
Items.Add(_dataModelConditionsVmFactory.DataModelConditionEventViewModel(dataModelConditionEvent, _modules));
break;
case DataModelConditionGeneralPredicate dataModelConditionGeneralPredicate:
Items.Add(_dataModelConditionsVmFactory.DataModelConditionGeneralPredicateViewModel(dataModelConditionGeneralPredicate, _modules));
break;
case DataModelConditionListPredicate dataModelConditionListPredicate:
Items.Add(_dataModelConditionsVmFactory.DataModelConditionListPredicateViewModel(dataModelConditionListPredicate, _modules));
break;
case DataModelConditionEventPredicate dataModelConditionEventPredicate:
Items.Add(_dataModelConditionsVmFactory.DataModelConditionEventPredicateViewModel(dataModelConditionEventPredicate, _modules));
break;
}
}
// Ensure the items are in the same order as on the model
((BindableCollection<DataModelConditionViewModel>) Items).Sort(i => Model.Children.IndexOf(i.Model));
foreach (DataModelConditionViewModel childViewModel in Items)
childViewModel.Update();
IsEventGroup = Items.Any(i => i is DataModelConditionEventViewModel);
if (IsEventGroup)
{
if (DataModelConditionGroup.BooleanOperator != BooleanOperator.And)
SelectBooleanOperator("And");
}
OnUpdated();
}
public override void Evaluate()
{
IsConditionMet = DataModelConditionGroup.Evaluate();
foreach (DataModelConditionViewModel dataModelConditionViewModel in Items)
dataModelConditionViewModel.Evaluate();
}
public override void UpdateModules()
{
foreach (DataModelConditionViewModel dataModelConditionViewModel in Items)
dataModelConditionViewModel.UpdateModules();
}
public void ConvertToConditionList(DataModelConditionViewModel predicateViewModel)
{
// Store the old index and remove the old predicate
int index = DataModelConditionGroup.Children.IndexOf(predicateViewModel.Model);
DataModelConditionGroup.RemoveChild(predicateViewModel.Model);
// Insert a list in the same position
DataModelConditionList list = new(DataModelConditionGroup);
list.UpdateList(predicateViewModel.LeftSideSelectionViewModel.DataModelPath);
DataModelConditionGroup.AddChild(list, index);
// Update to switch the VMs
Update();
}
public void ConvertToPredicate(DataModelConditionViewModel listViewModel)
{
// Store the old index and remove the old predicate
int index = DataModelConditionGroup.Children.IndexOf(listViewModel.Model);
DataModelConditionGroup.RemoveChild(listViewModel.Model);
// Insert a list in the same position
DataModelConditionGeneralPredicate predicate = new(DataModelConditionGroup, ProfileRightSideType.Dynamic);
predicate.UpdateLeftSide(listViewModel.LeftSideSelectionViewModel.DataModelPath);
DataModelConditionGroup.AddChild(predicate, index);
// Update to switch the VMs
Update();
}
private void CoreServiceOnFrameRendered(object sender, FrameRenderedEventArgs e)
{
if (IsRootGroup)
Evaluate();
}
public event EventHandler Updated;
#region Overrides of Screen
/// <inheritdoc />
protected override void OnInitialActivate()
{
base.OnInitialActivate();
Update();
_coreService.FrameRendered += CoreServiceOnFrameRendered;
}
/// <inheritdoc />
protected override void OnClose()
{
_coreService.FrameRendered -= CoreServiceOnFrameRendered;
base.OnClose();
}
#endregion
protected virtual void OnUpdated()
{
Updated?.Invoke(this, EventArgs.Empty);
}
}
public enum ConditionGroupType
{
General,
List,
Event
}
}
@@ -1,110 +0,0 @@
<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>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<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 LeftSideSelectionViewModel}"
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"
ToolTip="Change the list operator, determining how the list contents should match">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Any"
Command="{s:Action SelectListOperator}"
CommandParameter="Any"
ToolTip="Any of the list items should evaluate to true" />
<MenuItem Header="All"
Command="{s:Action SelectListOperator}"
CommandParameter="All"
ToolTip="All of the list items should evaluate to true" />
<MenuItem Header="None"
Command="{s:Action SelectListOperator}"
CommandParameter="None"
ToolTip="None of the list items should evaluate to true" />
<MenuItem Header="Count (NYI)"
Command="{s:Action SelectListOperator}"
CommandParameter="Count"
IsEnabled="False"
ToolTip="A specific amount of the list items should evaluate to true" />
</ContextMenu>
</Button.ContextMenu>
</Button>
<ToggleButton Grid.Row="0"
Grid.Column="3"
Style="{StaticResource MaterialDesignActionToggleButton}"
Focusable="False"
IsHitTestVisible="False"
IsChecked="{Binding IsConditionMet}"
Width="22"
Height="22"
HorizontalAlignment="Left">
<ToggleButton.Content>
<Border Background="#E74C4C" Width="22" Height="22">
<materialDesign:PackIcon Kind="Close" VerticalAlignment="Center" HorizontalAlignment="Center" Width="16" Height="16"/>
</Border>
</ToggleButton.Content>
<materialDesign:ToggleButtonAssist.OnContent>
<materialDesign:PackIcon Kind="Check" Width="16" Height="16"/>
</materialDesign:ToggleButtonAssist.OnContent>
</ToggleButton>
<ItemsControl Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3" ItemsSource="{Binding Items}" Margin="0 4 0 0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
@@ -1,26 +0,0 @@
using System.Windows;
using System.Windows.Controls;
namespace Artemis.UI.Screens.ProfileEditor.Conditions
{
/// <summary>
/// Interaction logic for DataModelConditionListView.xaml
/// </summary>
public partial class DataModelConditionListView : UserControl
{
public DataModelConditionListView()
{
InitializeComponent();
}
private void PropertyButton_OnClick(object sender, RoutedEventArgs e)
{
// DataContext is not set when using left button, I don't know why but there it is
if (sender is Button button && button.ContextMenu != null)
{
button.ContextMenu.DataContext = button.DataContext;
button.ContextMenu.IsOpen = true;
}
}
}
}
@@ -1,165 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Media;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Humanizer;
namespace Artemis.UI.Screens.ProfileEditor.Conditions
{
public sealed class DataModelConditionListViewModel : DataModelConditionViewModel, IDisposable
{
private readonly IDataModelConditionsVmFactory _dataModelConditionsVmFactory;
private readonly IDataModelUIService _dataModelUIService;
private readonly List<Module> _modules;
private readonly IProfileEditorService _profileEditorService;
public DataModelConditionListViewModel(DataModelConditionList dataModelConditionList,
List<Module> modules,
IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService,
IDataModelConditionsVmFactory dataModelConditionsVmFactory) : base(dataModelConditionList)
{
_modules = modules;
_profileEditorService = profileEditorService;
_dataModelUIService = dataModelUIService;
_dataModelConditionsVmFactory = dataModelConditionsVmFactory;
}
public DataModelConditionList DataModelConditionList => (DataModelConditionList) Model;
public string SelectedListOperator => DataModelConditionList.ListOperator.Humanize();
public void SelectListOperator(string type)
{
ListOperator enumValue = Enum.Parse<ListOperator>(type);
DataModelConditionList.ListOperator = enumValue;
NotifyOfPropertyChange(nameof(SelectedListOperator));
_profileEditorService.SaveSelectedProfileElement();
}
public void AddCondition()
{
DataModelConditionList.AddChild(new DataModelConditionGeneralPredicate(DataModelConditionList, ProfileRightSideType.Dynamic));
Update();
_profileEditorService.SaveSelectedProfileElement();
}
public void AddGroup()
{
DataModelConditionList.AddChild(new DataModelConditionGroup(DataModelConditionList));
Update();
_profileEditorService.SaveSelectedProfileElement();
}
public override void Evaluate()
{
IsConditionMet = DataModelConditionList.Evaluate();
foreach (DataModelConditionViewModel dataModelConditionViewModel in Items)
dataModelConditionViewModel.Evaluate();
}
public override void Delete()
{
base.Delete();
_profileEditorService.SaveSelectedProfileElement();
}
/// <inheritdoc />
public override void UpdateModules()
{
foreach (DataModelConditionViewModel dataModelConditionViewModel in Items)
dataModelConditionViewModel.UpdateModules();
}
public void Initialize()
{
LeftSideSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_modules);
LeftSideSelectionViewModel.PropertySelected += LeftSideSelectionViewModelOnPropertySelected;
IReadOnlyCollection<DataModelVisualizationRegistration> editors = _dataModelUIService.RegisteredDataModelEditors;
List<Type> supportedInputTypes = editors.Select(e => e.SupportedType).ToList();
supportedInputTypes.AddRange(editors.Where(e => e.CompatibleConversionTypes != null).SelectMany(e => e.CompatibleConversionTypes));
supportedInputTypes.Add(typeof(IEnumerable<>));
LeftSideSelectionViewModel.FilterTypes = supportedInputTypes.ToArray();
LeftSideSelectionViewModel.ButtonBrush = new SolidColorBrush(Color.FromRgb(71, 108, 188));
LeftSideSelectionViewModel.Placeholder = "Select a list";
Update();
}
public void ApplyList()
{
Type newType = LeftSideSelectionViewModel.DataModelPath.GetPropertyType();
bool converted = ConvertIfRequired(newType);
if (converted)
return;
DataModelConditionList.UpdateList(LeftSideSelectionViewModel.DataModelPath);
_profileEditorService.SaveSelectedProfileElement();
Update();
}
public override void Update()
{
LeftSideSelectionViewModel.ChangeDataModelPath(DataModelConditionList.ListPath);
NotifyOfPropertyChange(nameof(SelectedListOperator));
// Remove VMs of effects no longer applied on the layer
Items.RemoveRange(Items.Where(c => !DataModelConditionList.Children.Contains(c.Model)).ToList());
if (DataModelConditionList.ListPath == null || !DataModelConditionList.ListPath.IsValid)
return;
List<DataModelConditionViewModel> viewModels = new();
foreach (DataModelConditionPart childModel in Model.Children)
{
if (Items.Any(c => c.Model == childModel))
continue;
if (!(childModel is DataModelConditionGroup dataModelConditionGroup))
continue;
DataModelConditionGroupViewModel viewModel = _dataModelConditionsVmFactory.DataModelConditionGroupViewModel(dataModelConditionGroup, ConditionGroupType.List, _modules);
viewModel.IsRootGroup = true;
viewModels.Add(viewModel);
}
if (viewModels.Any())
Items.AddRange(viewModels);
foreach (DataModelConditionViewModel childViewModel in Items)
childViewModel.Update();
}
protected override void OnInitialActivate()
{
Initialize();
base.OnInitialActivate();
}
private void LeftSideSelectionViewModelOnPropertySelected(object sender, DataModelInputDynamicEventArgs e)
{
ApplyList();
}
#region IDisposable
public void Dispose()
{
LeftSideSelectionViewModel.Dispose();
LeftSideSelectionViewModel.PropertySelected -= LeftSideSelectionViewModelOnPropertySelected;
}
#endregion
}
}
@@ -1,95 +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:utilities="clr-namespace:Artemis.UI.Utilities"
xmlns:DataModelConditions="clr-namespace:Artemis.UI.Screens.ProfileEditor.Conditions"
x:Class="Artemis.UI.Screens.ProfileEditor.Conditions.DataModelConditionEventPredicateView"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance IsDesignTimeCreatable=False, Type={x:Type DataModelConditions:DataModelConditionEventPredicateViewModel}}">
<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 0 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 -->
<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}"
IsEnabled="{Binding CanSelectOperator}"
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>
@@ -1,26 +0,0 @@
using System.Windows;
using System.Windows.Controls;
namespace Artemis.UI.Screens.ProfileEditor.Conditions
{
/// <summary>
/// Interaction logic for DataModelConditionEventPredicateView.xaml
/// </summary>
public partial class DataModelConditionEventPredicateView : UserControl
{
public DataModelConditionEventPredicateView()
{
InitializeComponent();
}
private void PropertyButton_OnClick(object sender, RoutedEventArgs e)
{
// DataContext is not set when using left button, I don't know why but there it is
if (sender is Button button && button.ContextMenu != null)
{
button.ContextMenu.DataContext = button.DataContext;
button.ContextMenu.IsOpen = true;
}
}
}
}
@@ -1,82 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Media;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.Core.Services;
using Artemis.UI.Extensions;
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.Conditions
{
public class DataModelConditionEventPredicateViewModel : DataModelConditionPredicateViewModel
{
private readonly IDataModelUIService _dataModelUIService;
public DataModelConditionEventPredicateViewModel(DataModelConditionEventPredicate dataModelConditionEventPredicate,
List<Module> modules,
IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService,
IConditionOperatorService conditionOperatorService,
ISettingsService settingsService)
: base(dataModelConditionEventPredicate, modules, profileEditorService, dataModelUIService, conditionOperatorService, settingsService)
{
_dataModelUIService = dataModelUIService;
LeftSideColor = new SolidColorBrush(Color.FromRgb(185, 164, 10));
}
public DataModelConditionEventPredicate DataModelConditionEventPredicate => (DataModelConditionEventPredicate) Model;
public override void Initialize()
{
base.Initialize();
DataModelPropertiesViewModel eventDataModel = GetEventDataModel();
LeftSideSelectionViewModel.ChangeDataModel(eventDataModel);
}
protected override void OnInitialActivate()
{
base.OnInitialActivate();
Initialize();
}
protected override List<Type> GetSupportedInputTypes()
{
IReadOnlyCollection<DataModelVisualizationRegistration> editors = _dataModelUIService.RegisteredDataModelEditors;
List<Type> supportedInputTypes = editors.Select(e => e.SupportedType).ToList();
supportedInputTypes.AddRange(editors.Where(e => e.CompatibleConversionTypes != null).SelectMany(e => e.CompatibleConversionTypes));
return supportedInputTypes;
}
protected override Type GetLeftSideType()
{
return LeftSideSelectionViewModel.DataModelPath?.GetPropertyType();
}
protected override List<DataModelPropertiesViewModel> GetExtraRightSideDataModelViewModels()
{
// Only the VM housing the event arguments is expected here which is a DataModelPropertiesViewModel
return GetEventDataModel().Children.Cast<DataModelPropertiesViewModel>().ToList();
}
private DataModelPropertiesViewModel GetEventDataModel()
{
EventPredicateWrapperDataModel wrapper = EventPredicateWrapperDataModel.Create(
DataModelConditionEventPredicate.DataModelConditionEvent.EventArgumentType
);
return wrapper.CreateViewModel(_dataModelUIService, new DataModelUpdateConfiguration(false));
}
public override void Evaluate()
{
}
}
}
@@ -1,115 +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:utilities="clr-namespace:Artemis.UI.Utilities"
xmlns:conditions="clr-namespace:Artemis.UI.Screens.ProfileEditor.Conditions"
x:Class="Artemis.UI.Screens.ProfileEditor.Conditions.DataModelConditionGeneralPredicateView"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance IsDesignTimeCreatable=False, Type={x:Type conditions:DataModelConditionGeneralPredicateViewModel}}">
<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 0 0 3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<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}"
IsEnabled="{Binding CanSelectOperator}"
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" />
<ToggleButton Grid.Row="0"
Grid.Column="4"
Style="{StaticResource MaterialDesignActionToggleButton}"
Focusable="False"
IsHitTestVisible="False"
IsChecked="{Binding IsConditionMet}"
Width="22"
Height="22"
HorizontalAlignment="Left">
<ToggleButton.Content>
<Border Background="#E74C4C" Width="22" Height="22">
<materialDesign:PackIcon Kind="Close" VerticalAlignment="Center" HorizontalAlignment="Center" Width="16" Height="16"/>
</Border>
</ToggleButton.Content>
<materialDesign:ToggleButtonAssist.OnContent>
<materialDesign:PackIcon Kind="Check" Width="16" Height="16"/>
</materialDesign:ToggleButtonAssist.OnContent>
</ToggleButton>
</Grid>
</UserControl>
@@ -1,26 +0,0 @@
using System.Windows;
using System.Windows.Controls;
namespace Artemis.UI.Screens.ProfileEditor.Conditions
{
/// <summary>
/// Interaction logic for DataModelConditionGeneralPredicateView.xaml
/// </summary>
public partial class DataModelConditionGeneralPredicateView : UserControl
{
public DataModelConditionGeneralPredicateView()
{
InitializeComponent();
}
private void PropertyButton_OnClick(object sender, RoutedEventArgs e)
{
// DataContext is not set when using left button, I don't know why but there it is
if (sender is Button button && button.ContextMenu != null)
{
button.ContextMenu.DataContext = button.DataContext;
button.ContextMenu.IsOpen = true;
}
}
}
}
@@ -1,53 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.Core.Services;
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
namespace Artemis.UI.Screens.ProfileEditor.Conditions
{
public class DataModelConditionGeneralPredicateViewModel : DataModelConditionPredicateViewModel
{
private readonly IDataModelUIService _dataModelUIService;
public DataModelConditionGeneralPredicateViewModel(DataModelConditionGeneralPredicate dataModelConditionGeneralPredicate,
List<Module> modules,
IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService,
IConditionOperatorService conditionOperatorService,
ISettingsService settingsService)
: base(dataModelConditionGeneralPredicate, modules, profileEditorService, dataModelUIService, conditionOperatorService, settingsService)
{
_dataModelUIService = dataModelUIService;
}
protected override void OnInitialActivate()
{
Initialize();
}
protected override List<Type> GetSupportedInputTypes()
{
IReadOnlyCollection<DataModelVisualizationRegistration> editors = _dataModelUIService.RegisteredDataModelEditors;
List<Type> supportedInputTypes = editors.Select(e => e.SupportedType).ToList();
supportedInputTypes.AddRange(editors.Where(e => e.CompatibleConversionTypes != null).SelectMany(e => e.CompatibleConversionTypes));
supportedInputTypes.Add(typeof(IEnumerable<>));
return supportedInputTypes;
}
protected override Type GetLeftSideType()
{
return LeftSideSelectionViewModel.DataModelPath?.GetPropertyType();
}
public override void Evaluate()
{
IsConditionMet = DataModelConditionPredicate.Evaluate();
}
}
}
@@ -1,95 +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: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>
<utilities:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Margin="0 0 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}"
IsEnabled="{Binding CanSelectOperator}"
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>
@@ -1,26 +0,0 @@
using System.Windows;
using System.Windows.Controls;
namespace Artemis.UI.Screens.ProfileEditor.Conditions
{
/// <summary>
/// Interaction logic for DataModelConditionListPredicateView.xaml
/// </summary>
public partial class DataModelConditionListPredicateView : UserControl
{
public DataModelConditionListPredicateView()
{
InitializeComponent();
}
private void PropertyButton_OnClick(object sender, RoutedEventArgs e)
{
// DataContext is not set when using left button, I don't know why but there it is
if (sender is Button button && button.ContextMenu != null)
{
button.ContextMenu.DataContext = button.DataContext;
button.ContextMenu.IsOpen = true;
}
}
}
}
@@ -1,97 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Media;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.Core.Services;
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
namespace Artemis.UI.Screens.ProfileEditor.Conditions
{
public class DataModelConditionListPredicateViewModel : DataModelConditionPredicateViewModel
{
private readonly IDataModelUIService _dataModelUIService;
public DataModelConditionListPredicateViewModel(DataModelConditionListPredicate dataModelConditionListPredicate,
List<Module> modules,
IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService,
IConditionOperatorService conditionOperatorService,
ISettingsService settingsService)
: base(dataModelConditionListPredicate, modules, profileEditorService, dataModelUIService, conditionOperatorService, settingsService)
{
_dataModelUIService = dataModelUIService;
LeftSideColor = new SolidColorBrush(Color.FromRgb(71, 108, 188));
}
public DataModelConditionListPredicate DataModelConditionListPredicate => (DataModelConditionListPredicate) Model;
public override void Initialize()
{
base.Initialize();
DataModelPropertiesViewModel listDataModel = GetListDataModel();
LeftSideSelectionViewModel.ChangeDataModel(listDataModel);
// If this is a primitive list the user doesn't have much to choose, so preselect the list item for them
if (DataModelConditionListPredicate.DataModelConditionList.IsPrimitiveList && DataModelConditionListPredicate.LeftPath == null)
{
DataModelConditionListPredicate.UpdateLeftSide(listDataModel.Children.FirstOrDefault()?.DataModelPath);
Update();
}
}
public override void Evaluate()
{
}
public override void UpdateModules()
{
foreach (DataModelConditionViewModel dataModelConditionViewModel in Items)
dataModelConditionViewModel.UpdateModules();
}
protected override void OnInitialActivate()
{
base.OnInitialActivate();
Initialize();
}
protected override List<Type> GetSupportedInputTypes()
{
IReadOnlyCollection<DataModelVisualizationRegistration> editors = _dataModelUIService.RegisteredDataModelEditors;
List<Type> supportedInputTypes = editors.Select(e => e.SupportedType).ToList();
supportedInputTypes.AddRange(editors.Where(e => e.CompatibleConversionTypes != null).SelectMany(e => e.CompatibleConversionTypes));
return supportedInputTypes;
}
protected override Type GetLeftSideType()
{
return DataModelConditionListPredicate.DataModelConditionList.IsPrimitiveList
? DataModelConditionListPredicate.DataModelConditionList.ListType
: LeftSideSelectionViewModel.DataModelPath?.GetPropertyType();
}
protected override List<DataModelPropertiesViewModel> GetExtraRightSideDataModelViewModels()
{
if (GetListDataModel()?.Children?.FirstOrDefault() is DataModelPropertiesViewModel listValue)
return new List<DataModelPropertiesViewModel> {listValue};
return null;
}
private DataModelPropertiesViewModel GetListDataModel()
{
ListPredicateWrapperDataModel wrapper = ListPredicateWrapperDataModel.Create(
DataModelConditionListPredicate.DataModelConditionList.ListType!,
DataModelConditionListPredicate.DataModelConditionList.ListPath?.GetPropertyDescription()?.ListItemName
);
return wrapper.CreateViewModel(_dataModelUIService, new DataModelUpdateConfiguration(true));
}
}
}
@@ -8,13 +8,15 @@
xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:displayConditions="clr-namespace:Artemis.UI.Screens.ProfileEditor.DisplayConditions"
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
xmlns:controls="clr-namespace:Artemis.VisualScripting.Editor.Controls;assembly=Artemis.VisualScripting"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
x:Class="Artemis.UI.Screens.ProfileEditor.DisplayConditions.DisplayConditionsView"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance {x:Type displayConditions:DisplayConditionsViewModel}}">
<UserControl.Resources>
<converters:InverseBooleanConverter x:Key="InverseBooleanConverter" />
<converters:ComparisonConverter x:Key="ComparisonConverter" />
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
</UserControl.Resources>
<Grid Margin="10">
@@ -22,255 +24,95 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" Margin="0 -2">
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}" VerticalAlignment="Center">
Display conditions
</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignSubtitle2TextBlock}"
Foreground="{DynamicResource MaterialDesignBodyLight}"
VerticalAlignment="Center"
HorizontalAlignment="Right"
FontSize="13">
Not applied during editing
</TextBlock>
<materialDesign:ColorZone Mode="Standard" CornerRadius="3" Margin="0 -2" HorizontalAlignment="Right">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<RadioButton Grid.Column="0"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding Path=DisplayConditionType, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static displayConditions:DisplayConditionType.None}}"
MinWidth="0"
Padding="10 0">
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
No condition, the element is always displayed
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="No" VerticalAlignment="Center" Margin="0 0 2 -3" />
NONE
</TextBlock>
</RadioButton>
<RadioButton Grid.Column="1"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding Path=DisplayConditionType, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static displayConditions:DisplayConditionType.Static}}"
MinWidth="0"
Padding="10 0">
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
A condition that constantly checks and is either 'on' or 'off'
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="Check" VerticalAlignment="Center" Margin="0 0 2 -3" />
CONSTANT
</TextBlock>
</RadioButton>
<RadioButton Grid.Column="2"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding Path=DisplayConditionType, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static displayConditions:DisplayConditionType.Events}}"
MinWidth="0"
Padding="10 0">
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
A condition triggers on a selectable event
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="LightningBolt" VerticalAlignment="Center" Margin="0 0 2 -3" />
EVENT
</TextBlock>
</RadioButton>
</Grid>
</materialDesign:ColorZone>
</DockPanel>
<Separator Grid.Row="1" Grid.Column="0" Style="{StaticResource MaterialDesignDarkSeparator}" Margin="-2 0" />
<Grid Grid.Row="2" Grid.Column="0">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Background="{StaticResource MaterialDesignCardBackground}">
<ContentControl s:View.Model="{Binding ActiveItem}" />
</ScrollViewer>
<ContentControl Grid.Row="2"
s:View.Model="{Binding ActiveItem}"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsTabStop="False"
Visibility="{Binding ActiveItem, Converter={StaticResource NullToVisibilityConverter}}"/>
<materialDesign:Card
Visibility="{Binding DisplayStartHint, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}"
VerticalAlignment="Top"
Background="{DynamicResource PrimaryHueDarkBrush}"
Foreground="{DynamicResource PrimaryHueDarkForegroundBrush}"
Margin="5 30 5 0"
Padding="8"
Width="315"
HorizontalAlignment="Left">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<materialDesign:PackIcon Kind="Redo" Margin="0 0 5 0">
<materialDesign:PackIcon.RenderTransform>
<RotateTransform Angle="225" CenterX="8" CenterY="8" />
</materialDesign:PackIcon.RenderTransform>
</materialDesign:PackIcon>
<TextBlock Grid.Column="1" VerticalAlignment="Center">Click the plus icon to start using display conditions!</TextBlock>
</Grid>
</materialDesign:Card>
</Grid>
<Grid Grid.Row="3" Visibility="{Binding IsEventCondition, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}">
<Grid.RowDefinitions>
<RowDefinition Height="18" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MinWidth="140" />
<ColumnDefinition Width="*" MinWidth="170" />
</Grid.ColumnDefinitions>
<!-- Play mode -->
<TextBlock Grid.Column="0" Text="Play mode">
<TextBlock.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-30">
<TextBlock>
Configure how the layer should act while the conditions above are met
</TextBlock>
</ToolTip>
</TextBlock.ToolTip>
<StackPanel Grid.Row="2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Visibility="{Binding ActiveItem, ConverterParameter=Inverted, Converter={StaticResource NullToVisibilityConverter}}">
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" TextWrapping="Wrap" TextAlignment="Center" Margin="0 10">
Display conditions disabled
<materialDesign:PackIcon Kind="No" Margin="0 0 0 -6" Width="30" Height="30" VerticalAlignment="Center" />
</TextBlock>
<materialDesign:ColorZone Grid.Row="1" Grid.Column="0" Mode="Standard" CornerRadius="3" Margin="0 0 2 0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<RadioButton Grid.Column="0"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding DisplayContinuously}"
MinWidth="0"
Padding="5 0">
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
Continue repeating the main segment of the timeline while the condition is met
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="Repeat" VerticalAlignment="Center" Margin="-3 0 0 -3" />
REPEAT
</TextBlock>
</RadioButton>
<RadioButton Grid.Column="1"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding DisplayContinuously, Converter={StaticResource InverseBooleanConverter}}"
MinWidth="0"
Padding="5 0">
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
Only play the timeline once when the condition is met
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="StopwatchOutline" VerticalAlignment="Center" Margin="-3 0 0 -3" />
ONCE
</TextBlock>
</RadioButton>
</Grid>
</materialDesign:ColorZone>
<!-- Stop mode -->
<TextBlock Grid.Row="0" Grid.Column="1" Text="Stop mode">
<TextBlock.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-30">
<TextBlock>
Configure how the layer should act when the conditions above are no longer met
</TextBlock>
</ToolTip>
</TextBlock.ToolTip>
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}" TextWrapping="Wrap" TextAlignment="Center">
This element will always display.
</TextBlock>
<materialDesign:ColorZone Grid.Row="1" Grid.Column="1" Mode="Standard" CornerRadius="3" Margin="2 0 0 0" HorizontalAlignment="Stretch">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition MinWidth="100" />
</Grid.ColumnDefinitions>
<RadioButton Grid.Column="0"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding AlwaysFinishTimeline}"
MinWidth="0"
Padding="5 0">
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
When conditions are no longer met, finish the the current run of the main timeline
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="PlayOutline" VerticalAlignment="Center" Margin="-3 0 0 -3" />
FINISH
</TextBlock>
</RadioButton>
<RadioButton Grid.Column="1"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding AlwaysFinishTimeline, Converter={StaticResource InverseBooleanConverter}}"
MinWidth="0"
Padding="5 0">
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
When conditions are no longer met, skip to the end segment of the timeline
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="SkipNextOutline" VerticalAlignment="Center" Margin="-3 0 0 -3" />
SKIP TO END
</TextBlock>
</RadioButton>
</Grid>
</materialDesign:ColorZone>
</Grid>
<Grid Grid.Row="3" Visibility="{Binding IsEventCondition, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
<Grid.RowDefinitions>
<RowDefinition Height="18" />
<RowDefinition />
</Grid.RowDefinitions>
<!-- Trigger mode -->
<TextBlock Grid.Column="0" Text="Trigger mode">
<TextBlock.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-30">
<TextBlock>
Configure how the layer should act when the event(s) trigger
</TextBlock>
</ToolTip>
</TextBlock.ToolTip>
</TextBlock>
<materialDesign:ColorZone Grid.Row="1" Grid.Column="0" Mode="Standard" CornerRadius="3">
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<RadioButton Grid.Column="0"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding Path=EventOverlapMode, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Restart}}">
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="Repeat" VerticalAlignment="Center" Margin="-3 0 0 -3" />
RESTART
</TextBlock>
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
Stop the current run and restart the timeline
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
</RadioButton>
<RadioButton Grid.Column="1"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding Path=EventOverlapMode, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Toggle}}">
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="TrafficLight" VerticalAlignment="Center" Margin="-3 0 0 -3" />
TOGGLE
</TextBlock>
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
Repeat the timeline until the event fires again
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
</RadioButton>
<RadioButton Grid.Column="2"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding Path=EventOverlapMode, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Ignore}}">
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="EarHearingOff" VerticalAlignment="Center" Margin="-3 0 0 -3" />
IGNORE
</TextBlock>
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
Ignore subsequent event fires until the timeline finishes
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
</RadioButton>
<RadioButton Grid.Column="3"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding Path=EventOverlapMode, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Copy}}">
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="ContentCopy" VerticalAlignment="Center" Margin="-3 0 0 -3" />
COPY
</TextBlock>
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
Play another copy of the timeline on top of the current run
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
</RadioButton>
</Grid>
</materialDesign:ColorZone>
</Grid>
</StackPanel>
</Grid>
</UserControl>
@@ -1,95 +1,38 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.Core;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.Conditions;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
{
public class DisplayConditionsViewModel : Conductor<DataModelConditionGroupViewModel>, IProfileEditorPanelViewModel
public class DisplayConditionsViewModel : Conductor<Screen>, IProfileEditorPanelViewModel
{
private readonly IDataModelConditionsVmFactory _dataModelConditionsVmFactory;
private readonly IConditionVmFactory _conditionVmFactory;
private readonly IProfileEditorService _profileEditorService;
private RenderProfileElement _renderProfileElement;
private bool _displayStartHint;
private bool _isEventCondition;
private DisplayConditionType _displayConditionType;
private StaticCondition _staticCondition;
private EventCondition _eventCondition;
public DisplayConditionsViewModel(IProfileEditorService profileEditorService, IDataModelConditionsVmFactory dataModelConditionsVmFactory)
public DisplayConditionsViewModel(IProfileEditorService profileEditorService, IConditionVmFactory conditionVmFactory)
{
_profileEditorService = profileEditorService;
_dataModelConditionsVmFactory = dataModelConditionsVmFactory;
_conditionVmFactory = conditionVmFactory;
}
public bool DisplayStartHint
public DisplayConditionType DisplayConditionType
{
get => _displayStartHint;
set => SetAndNotify(ref _displayStartHint, value);
}
public bool IsEventCondition
{
get => _isEventCondition;
set => SetAndNotify(ref _isEventCondition, value);
}
public RenderProfileElement RenderProfileElement
{
get => _renderProfileElement;
get => _displayConditionType;
set
{
if (!SetAndNotify(ref _renderProfileElement, value)) return;
NotifyOfPropertyChange(nameof(DisplayContinuously));
NotifyOfPropertyChange(nameof(AlwaysFinishTimeline));
NotifyOfPropertyChange(nameof(EventOverlapMode));
if (!SetAndNotify(ref _displayConditionType, value)) return;
ChangeConditionType();
}
}
public bool DisplayContinuously
{
get => RenderProfileElement?.Timeline.PlayMode == TimelinePlayMode.Repeat;
set
{
TimelinePlayMode playMode = value ? TimelinePlayMode.Repeat : TimelinePlayMode.Once;
if (RenderProfileElement == null || RenderProfileElement?.Timeline.PlayMode == playMode) return;
RenderProfileElement.Timeline.PlayMode = playMode;
_profileEditorService.SaveSelectedProfileElement();
}
}
public bool AlwaysFinishTimeline
{
get => RenderProfileElement?.Timeline.StopMode == TimelineStopMode.Finish;
set
{
TimelineStopMode stopMode = value ? TimelineStopMode.Finish : TimelineStopMode.SkipToEnd;
if (RenderProfileElement == null || RenderProfileElement?.Timeline.StopMode == stopMode) return;
RenderProfileElement.Timeline.StopMode = stopMode;
_profileEditorService.SaveSelectedProfileElement();
}
}
public TimeLineEventOverlapMode EventOverlapMode
{
get => RenderProfileElement?.Timeline.EventOverlapMode ?? TimeLineEventOverlapMode.Restart;
set
{
if (RenderProfileElement == null || RenderProfileElement?.Timeline.EventOverlapMode == value) return;
RenderProfileElement.Timeline.EventOverlapMode = value;
_profileEditorService.SaveSelectedProfileElement();
}
}
public bool ConditionBehaviourEnabled => RenderProfileElement != null;
protected override void OnInitialActivate()
{
_profileEditorService.SelectedProfileElementChanged += SelectedProfileEditorServiceOnSelectedProfileElementChanged;
_profileEditorService.SelectedProfileElementChanged += ProfileEditorServiceOnSelectedProfileElementChanged;
Update(_profileEditorService.SelectedProfileElement);
base.OnInitialActivate();
@@ -97,70 +40,75 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
protected override void OnClose()
{
_profileEditorService.SelectedProfileElementChanged -= SelectedProfileEditorServiceOnSelectedProfileElementChanged;
_profileEditorService.SelectedProfileElementChanged -= ProfileEditorServiceOnSelectedProfileElementChanged;
base.OnClose();
}
private void SelectedProfileEditorServiceOnSelectedProfileElementChanged(object sender, RenderProfileElementEventArgs e)
private void ChangeConditionType()
{
Update(e.RenderProfileElement);
if (_profileEditorService.SelectedProfileElement == null)
return;
// Keep the old condition around in case the user changes their mind
if (_profileEditorService.SelectedProfileElement.DisplayCondition is StaticCondition staticCondition)
_staticCondition = staticCondition;
else if (_profileEditorService.SelectedProfileElement.DisplayCondition is EventCondition eventCondition)
_eventCondition = eventCondition;
// If we have the old condition around put it back
if (DisplayConditionType == DisplayConditionType.Static)
_profileEditorService.SelectedProfileElement.DisplayCondition = _staticCondition ?? new StaticCondition(_profileEditorService.SelectedProfileElement);
else if (DisplayConditionType == DisplayConditionType.Events)
_profileEditorService.SelectedProfileElement.DisplayCondition = _eventCondition ?? new EventCondition(_profileEditorService.SelectedProfileElement);
else
_profileEditorService.SelectedProfileElement.DisplayCondition = null;
_profileEditorService.SaveSelectedProfileElement();
Update(_profileEditorService.SelectedProfileElement);
}
private void Update(RenderProfileElement renderProfileElement)
{
if (RenderProfileElement != null)
{
RenderProfileElement.DisplayCondition.ChildAdded -= DisplayConditionOnChildrenModified;
RenderProfileElement.DisplayCondition.ChildRemoved -= DisplayConditionOnChildrenModified;
RenderProfileElement.Timeline.PropertyChanged -= TimelineOnPropertyChanged;
}
RenderProfileElement = renderProfileElement;
NotifyOfPropertyChange(nameof(DisplayContinuously));
NotifyOfPropertyChange(nameof(AlwaysFinishTimeline));
NotifyOfPropertyChange(nameof(ConditionBehaviourEnabled));
if (renderProfileElement == null)
{
ActiveItem = null;
return;
}
// Ensure the layer has a root display condition group
if (renderProfileElement.DisplayCondition == null)
renderProfileElement.DisplayCondition = new DataModelConditionGroup(null);
if (renderProfileElement.DisplayCondition is StaticCondition staticCondition)
{
ActiveItem = _conditionVmFactory.StaticConditionViewModel(staticCondition);
_displayConditionType = DisplayConditionType.Static;
}
else if (renderProfileElement.DisplayCondition is EventCondition eventsCondition)
{
ActiveItem = _conditionVmFactory.EventConditionViewModel(eventsCondition);
_displayConditionType = DisplayConditionType.Events;
}
else
{
ActiveItem = null;
_displayConditionType = DisplayConditionType.None;
}
List<Module> modules = new();
if (_profileEditorService.SelectedProfileConfiguration?.Module != null)
modules.Add(_profileEditorService.SelectedProfileConfiguration.Module);
ActiveItem = _dataModelConditionsVmFactory.DataModelConditionGroupViewModel(renderProfileElement.DisplayCondition, ConditionGroupType.General, modules);
ActiveItem.IsRootGroup = true;
DisplayStartHint = !RenderProfileElement.DisplayCondition.Children.Any();
IsEventCondition = RenderProfileElement.DisplayCondition.Children.Any(c => c is DataModelConditionEvent);
RenderProfileElement.DisplayCondition.ChildAdded += DisplayConditionOnChildrenModified;
RenderProfileElement.DisplayCondition.ChildRemoved += DisplayConditionOnChildrenModified;
RenderProfileElement.Timeline.PropertyChanged += TimelineOnPropertyChanged;
}
private void TimelineOnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
NotifyOfPropertyChange(nameof(DisplayContinuously));
NotifyOfPropertyChange(nameof(AlwaysFinishTimeline));
NotifyOfPropertyChange(nameof(EventOverlapMode));
NotifyOfPropertyChange(nameof(DisplayConditionType));
}
private void DisplayConditionOnChildrenModified(object sender, EventArgs e)
private void ProfileEditorServiceOnSelectedProfileElementChanged(object sender, RenderProfileElementEventArgs e)
{
DisplayStartHint = !RenderProfileElement.DisplayCondition.Children.Any();
IsEventCondition = RenderProfileElement.DisplayCondition.Children.Any(c => c is DataModelConditionEvent);
}
if (_staticCondition != null && e.PreviousRenderProfileElement?.DisplayCondition != _staticCondition)
_staticCondition.Dispose();
if (_eventCondition != null && e.PreviousRenderProfileElement?.DisplayCondition != _eventCondition)
_eventCondition.Dispose();
public void EventTriggerModeSelected()
{
_profileEditorService.SaveSelectedProfileElement();
Update(e.RenderProfileElement);
}
}
public enum DisplayConditionType
{
None,
Static,
Events
}
}
@@ -0,0 +1,204 @@
<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:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.DisplayConditions.Event"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:controls="clr-namespace:Artemis.VisualScripting.Editor.Controls;assembly=Artemis.VisualScripting"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
xmlns:shared="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
xmlns:Shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" x:Class="Artemis.UI.Screens.ProfileEditor.DisplayConditions.Event.EventConditionView"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance {x:Type local:EventConditionViewModel}}">
<UserControl.Resources>
<Shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<converters:ComparisonConverter x:Key="ComparisonConverter" />
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="22" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" VerticalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<shared:DataModelPicker Grid.Row="0"
Grid.Column="0"
Margin="0 0 0 5"
ButtonBrush="DarkGoldenrod"
DataModelPath="{Binding EventCondition.EventPath}"
DataModelPathSelected="{s:Action DataModelPathSelected}"
FilterTypes="{Binding FilterTypes}"
ShowFullPath="True"
Modules="{Binding Modules}" />
<CheckBox Grid.Row="0"
Grid.Column="1"
IsChecked="{Binding TriggerConditionally}"
Content="Conditional trigger"
VerticalAlignment="Center"
ToolTip="When enabled, the element will only trigger if the script evaluates to true"
Style="{StaticResource MaterialDesignDarkCheckBox}" />
<Grid Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
MouseUp="{s:Action ScriptGridMouseUp}"
Cursor="Hand"
Visibility="{Binding EventCondition.Script, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Normal, Mode=OneWay}">
<controls:VisualScriptPresenter Script="{Binding EventCondition.Script}" AutoFitScript="True" />
<Border VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Opacity="0">
<Border.Background>
<SolidColorBrush Color="{Binding Color, Source={StaticResource MaterialDesignCardBackground}}" Opacity="0.75" />
</Border.Background>
<Border.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type Grid}, Mode=FindAncestor}}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.25" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:0.25" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" VerticalAlignment="Center" TextAlignment="Center">
Click to edit script
<materialDesign:PackIcon Kind="OpenInNew" Margin="0 0 0 -6 " Width="30" Height="30" VerticalAlignment="Center" />
</TextBlock>
</Border>
</Grid>
<Grid Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
Visibility="{Binding EventCondition.Script, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted, Mode=OneWay}">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" TextWrapping="Wrap" TextAlignment="Center" Margin="0 10">
Conditional trigger disabled
<materialDesign:PackIcon Kind="No" Margin="0 0 0 -6" Width="30" Height="30" VerticalAlignment="Center" />
</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}" TextWrapping="Wrap" TextAlignment="Center">
This element will trigger any time the event fires.
</TextBlock>
</StackPanel>
</Grid>
</Grid>
<!-- Trigger mode -->
<TextBlock Grid.Row="1" Grid.Column="0" Text="Trigger mode" VerticalAlignment="Center">
<TextBlock.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-30">
<TextBlock>
<Run Text="Configure how the layer should act when the event(s) trigger" />
</TextBlock>
</ToolTip>
</TextBlock.ToolTip>
</TextBlock>
<materialDesign:ColorZone Grid.Row="2" Grid.Column="0" Mode="Standard" CornerRadius="3">
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<RadioButton Grid.Column="0"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding EventOverlapMode, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Restart}, Converter={StaticResource ComparisonConverter}}">
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
<Run Text="Stop the current run and restart the timeline" />
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
<TextBlock VerticalAlignment="Center" FontSize="12">
<InlineUIContainer>
<materialDesign:PackIcon Kind="Repeat" VerticalAlignment="Center" Margin="-3 0 0 -3" />
</InlineUIContainer>
<Run Text=" RESTART" />
</TextBlock>
</RadioButton>
<RadioButton Grid.Column="1"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding EventOverlapMode, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Toggle}, Converter={StaticResource ComparisonConverter}}">
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
<Run Text="Repeat the timeline until the event fires again" />
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
<TextBlock VerticalAlignment="Center" FontSize="12">
<InlineUIContainer>
<materialDesign:PackIcon Kind="TrafficLight" VerticalAlignment="Center" Margin="-3 0 0 -3" />
</InlineUIContainer>
<Run Text=" TOGGLE" />
</TextBlock>
</RadioButton>
<RadioButton Grid.Column="2"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding EventOverlapMode, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Ignore}, Converter={StaticResource ComparisonConverter}}">
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
<Run Text="Ignore subsequent event fires until the timeline finishes" />
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
<TextBlock VerticalAlignment="Center" FontSize="12">
<InlineUIContainer>
<materialDesign:PackIcon Kind="EarHearingOff" VerticalAlignment="Center" Margin="-3 0 0 -3" />
</InlineUIContainer>
<Run Text=" IGNORE" />
</TextBlock>
</RadioButton>
<RadioButton Grid.Column="3"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding EventOverlapMode, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Copy}, Converter={StaticResource ComparisonConverter}}">
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
<Run Text="Play another copy of the timeline on top of the current run" />
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
<TextBlock VerticalAlignment="Center" FontSize="12">
<InlineUIContainer>
<materialDesign:PackIcon Kind="ContentCopy" VerticalAlignment="Center" Margin="-3 0 0 -3" />
</InlineUIContainer>
<Run Text=" COPY" />
</TextBlock>
</RadioButton>
</Grid>
</materialDesign:ColorZone>
</Grid>
</UserControl>
@@ -0,0 +1,99 @@
using System;
using System.Linq;
using System.Windows.Input;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions.Event
{
public class EventConditionViewModel : Screen
{
private readonly INodeVmFactory _nodeVmFactory;
private readonly IProfileEditorService _profileEditorService;
private readonly IWindowManager _windowManager;
private NodeScript<bool> _oldScript;
public EventConditionViewModel(EventCondition eventCondition, IProfileEditorService profileEditorService, IWindowManager windowManager, INodeVmFactory nodeVmFactory)
{
_profileEditorService = profileEditorService;
_windowManager = windowManager;
_nodeVmFactory = nodeVmFactory;
EventCondition = eventCondition;
FilterTypes = new BindableCollection<Type> {typeof(IDataModelEvent)};
if (_profileEditorService.SelectedProfileConfiguration?.Module != null)
Modules = new BindableCollection<Module> {_profileEditorService.SelectedProfileConfiguration.Module};
}
public EventCondition EventCondition { get; }
public BindableCollection<Type> FilterTypes { get; }
public BindableCollection<Module> Modules { get; }
public TimeLineEventOverlapMode EventOverlapMode
{
get => EventCondition.EventOverlapMode;
set
{
if (EventCondition.EventOverlapMode == value) return;
EventCondition.EventOverlapMode = value;
_profileEditorService.SaveSelectedProfileElement();
}
}
public bool TriggerConditionally
{
get => EventCondition.Script != null;
set
{
if (EventCondition.Script != null)
{
_oldScript = EventCondition.Script;
EventCondition.Script = null;
}
else
{
if (_oldScript != null)
{
EventCondition.Script = _oldScript;
_oldScript = null;
}
else
{
EventCondition.CreateEmptyNodeScript();
}
}
}
}
#region Overrides of Screen
/// <inheritdoc />
protected override void OnClose()
{
_oldScript?.Dispose();
base.OnClose();
}
#endregion
public void DataModelPathSelected(object sender, DataModelSelectedEventArgs e)
{
EventCondition.UpdateEventNode();
DisplayName = EventCondition.EventPath?.Segments.LastOrDefault()?.GetPropertyDescription()?.Name ?? "Invalid event";
_profileEditorService.SaveSelectedProfileElement();
}
public void ScriptGridMouseUp(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton != MouseButton.Left)
return;
_windowManager.ShowDialog(_nodeVmFactory.NodeScriptWindowViewModel(EventCondition.Script));
_profileEditorService.SaveSelectedProfileElement();
}
}
}
@@ -0,0 +1,175 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.DisplayConditions.Static.StaticConditionView"
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.Static"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:controls="clr-namespace:Artemis.VisualScripting.Editor.Controls;assembly=Artemis.VisualScripting"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance {x:Type local:StaticConditionViewModel}}">
<UserControl.Resources>
<converters:InverseBooleanConverter x:Key="InverseBooleanConverter" />
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" MouseUp="{s:Action ScriptGridMouseUp}" Cursor="Hand">
<controls:VisualScriptPresenter Script="{Binding StaticCondition.Script}" AutoFitScript="True"/>
<Border Opacity="0">
<Border.Background>
<SolidColorBrush Color="{Binding Color, Source={StaticResource MaterialDesignCardBackground}}" Opacity="0.75" />
</Border.Background>
<Border.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type Grid}, Mode=FindAncestor}}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.25" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:0.25" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" VerticalAlignment="Center" TextAlignment="Center">
Click to edit script
<materialDesign:PackIcon Kind="OpenInNew" Margin="0 0 0 -6 " Width="30" Height="30" VerticalAlignment="Center" />
</TextBlock>
</Border>
</Grid>
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="22" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MinWidth="140" />
<ColumnDefinition Width="*" MinWidth="170" />
</Grid.ColumnDefinitions>
<!-- Play mode -->
<TextBlock Grid.Column="0" Text="Play mode" VerticalAlignment="Center">
<TextBlock.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-30">
<TextBlock>
Configure how the layer should act while the conditions above are met
</TextBlock>
</ToolTip>
</TextBlock.ToolTip>
</TextBlock>
<materialDesign:ColorZone Grid.Row="1" Grid.Column="0" Mode="Standard" CornerRadius="3" Margin="0 0 2 0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<RadioButton Grid.Column="0"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding DisplayContinuously}"
MinWidth="0"
Padding="5 0">
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
Continue repeating the main segment of the timeline while the condition is met
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="Repeat" VerticalAlignment="Center" Margin="-3 0 0 -3" />
REPEAT
</TextBlock>
</RadioButton>
<RadioButton Grid.Column="1"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding DisplayContinuously, Converter={StaticResource InverseBooleanConverter}}"
MinWidth="0"
Padding="5 0">
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
Only play the timeline once when the condition is met
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="StopwatchOutline" VerticalAlignment="Center" Margin="-3 0 0 -3" />
ONCE
</TextBlock>
</RadioButton>
</Grid>
</materialDesign:ColorZone>
<!-- Stop mode -->
<TextBlock Grid.Row="0" Grid.Column="1" Text="Stop mode">
<TextBlock.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-30">
<TextBlock>
Configure how the layer should act when the conditions above are no longer met
</TextBlock>
</ToolTip>
</TextBlock.ToolTip>
</TextBlock>
<materialDesign:ColorZone Grid.Row="1" Grid.Column="1" Mode="Standard" CornerRadius="3" Margin="2 0 0 0" HorizontalAlignment="Stretch">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition MinWidth="100" />
</Grid.ColumnDefinitions>
<RadioButton Grid.Column="0"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding AlwaysFinishTimeline}"
MinWidth="0"
Padding="5 0">
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
When conditions are no longer met, finish the the current run of the main timeline
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="PlayOutline" VerticalAlignment="Center" Margin="-3 0 0 -3" />
FINISH
</TextBlock>
</RadioButton>
<RadioButton Grid.Column="1"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding AlwaysFinishTimeline, Converter={StaticResource InverseBooleanConverter}}"
MinWidth="0"
Padding="5 0">
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
When conditions are no longer met, skip to the end segment of the timeline
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="SkipNextOutline" VerticalAlignment="Center" Margin="-3 0 0 -3" />
SKIP TO END
</TextBlock>
</RadioButton>
</Grid>
</materialDesign:ColorZone>
</Grid>
</Grid>
</UserControl>
@@ -0,0 +1,60 @@
using System.Windows.Input;
using Artemis.Core;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Shared.Services;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions.Static
{
public class StaticConditionViewModel : Screen
{
private readonly IWindowManager _windowManager;
private readonly INodeVmFactory _nodeVmFactory;
private readonly IProfileEditorService _profileEditorService;
private readonly RenderProfileElement _renderProfileElement;
public StaticConditionViewModel(StaticCondition staticCondition, IWindowManager windowManager, INodeVmFactory nodeVmFactory, IProfileEditorService profileEditorService)
{
_windowManager = windowManager;
_nodeVmFactory = nodeVmFactory;
_profileEditorService = profileEditorService;
_renderProfileElement = (RenderProfileElement) staticCondition.ProfileElement;
StaticCondition = staticCondition;
}
public StaticCondition StaticCondition { get; }
public bool DisplayContinuously
{
get => _renderProfileElement.Timeline.PlayMode == TimelinePlayMode.Repeat;
set
{
TimelinePlayMode playMode = value ? TimelinePlayMode.Repeat : TimelinePlayMode.Once;
if (_renderProfileElement.Timeline.PlayMode == playMode) return;
_renderProfileElement.Timeline.PlayMode = playMode;
_profileEditorService.SaveSelectedProfileElement();
}
}
public bool AlwaysFinishTimeline
{
get => _renderProfileElement?.Timeline.StopMode == TimelineStopMode.Finish;
set
{
TimelineStopMode stopMode = value ? TimelineStopMode.Finish : TimelineStopMode.SkipToEnd;
if (_renderProfileElement.Timeline.StopMode == stopMode) return;
_renderProfileElement.Timeline.StopMode = stopMode;
_profileEditorService.SaveSelectedProfileElement();
}
}
public void ScriptGridMouseUp(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton != MouseButton.Left)
return;
_windowManager.ShowDialog(_nodeVmFactory.NodeScriptWindowViewModel(StaticCondition.Script));
_profileEditorService.SaveSelectedProfileElement();
}
}
}
@@ -1,55 +0,0 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.ConditionalDataBinding.ConditionalDataBindingModeView"
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:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:dd="urn:gong-wpf-dragdrop"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DataModelConditions.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Grid.Row="0"
Style="{StaticResource DataModelConditionButton}"
Background="{StaticResource PrimaryHueMidBrush}"
BorderBrush="{StaticResource PrimaryHueMidBrush}"
HorizontalAlignment="Right"
Command="{s:Action AddCondition}"
Margin="0 5">
ADD CONDITION
</Button>
<ListBox Grid.Row="1"
ItemsSource="{Binding Items}"
materialDesign:RippleAssist.IsDisabled="True"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultDragAdorner="True"
HorizontalContentAlignment="Stretch"
VirtualizingPanel.ScrollUnit="Pixel"
Margin="-10 0 -10 -5">
<ListBox.ItemTemplate>
<DataTemplate>
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False"/>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
</UserControl>
@@ -1,131 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using Artemis.Core;
using Artemis.UI.Extensions;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Shared.Services;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.ConditionalDataBinding
{
public sealed class ConditionalDataBindingModeViewModel<TLayerProperty, TProperty> : Conductor<DataBindingConditionViewModel<TLayerProperty, TProperty>>.Collection.AllActive,
IDataBindingModeViewModel
{
private readonly IDataBindingsVmFactory _dataBindingsVmFactory;
private readonly IProfileEditorService _profileEditorService;
private bool _updating;
public ConditionalDataBindingModeViewModel(ConditionalDataBinding<TLayerProperty, TProperty> conditionalDataBinding,
IProfileEditorService profileEditorService,
IDataBindingsVmFactory dataBindingsVmFactory)
{
_profileEditorService = profileEditorService;
_dataBindingsVmFactory = dataBindingsVmFactory;
ConditionalDataBinding = conditionalDataBinding;
}
public ConditionalDataBinding<TLayerProperty, TProperty> ConditionalDataBinding { get; }
public void AddCondition()
{
DataBindingCondition<TLayerProperty, TProperty> condition = ConditionalDataBinding.AddCondition();
// Find the VM of the new condition
DataBindingConditionViewModel<TLayerProperty, TProperty> viewModel = Items.First(c => c.DataBindingCondition == condition);
viewModel.ActiveItem.AddCondition();
_profileEditorService.SaveSelectedProfileElement();
}
public void RemoveCondition(DataBindingCondition<TLayerProperty, TProperty> dataBindingCondition)
{
ConditionalDataBinding.RemoveCondition(dataBindingCondition);
}
protected override void OnInitialActivate()
{
Initialize();
base.OnInitialActivate();
}
private void UpdateItems()
{
_updating = true;
// Remove old VMs
List<DataBindingConditionViewModel<TLayerProperty, TProperty>> toRemove = Items.Where(c => !ConditionalDataBinding.Conditions.Contains(c.DataBindingCondition)).ToList();
foreach (DataBindingConditionViewModel<TLayerProperty, TProperty> dataBindingConditionViewModel in toRemove)
{
Items.Remove(dataBindingConditionViewModel);
dataBindingConditionViewModel.Dispose();
}
// Add missing VMs
foreach (DataBindingCondition<TLayerProperty, TProperty> condition in ConditionalDataBinding.Conditions)
if (Items.All(c => c.DataBindingCondition != condition))
Items.Add(_dataBindingsVmFactory.DataBindingConditionViewModel(condition));
// Fix order
((BindableCollection<DataBindingConditionViewModel<TLayerProperty, TProperty>>) Items).Sort(c => c.DataBindingCondition.Order);
_updating = false;
}
private void Initialize()
{
ConditionalDataBinding.ConditionsUpdated += ConditionalDataBindingOnConditionsUpdated;
Items.CollectionChanged += ItemsOnCollectionChanged;
UpdateItems();
}
private void ItemsOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (_updating || e.Action != NotifyCollectionChangedAction.Add)
return;
for (int index = 0; index < Items.Count; index++)
{
DataBindingConditionViewModel<TLayerProperty, TProperty> conditionViewModel = Items[index];
conditionViewModel.DataBindingCondition.Order = index + 1;
}
ConditionalDataBinding.ApplyOrder();
_profileEditorService.SaveSelectedProfileElement();
}
private void ConditionalDataBindingOnConditionsUpdated(object sender, EventArgs e)
{
UpdateItems();
}
public bool SupportsTestValue => false;
public void Update()
{
UpdateItems();
}
public object GetTestValue()
{
throw new NotSupportedException();
}
#region IDisposable
/// <inheritdoc />
public void Dispose()
{
ConditionalDataBinding.ConditionsUpdated -= ConditionalDataBindingOnConditionsUpdated;
foreach (DataBindingConditionViewModel<TLayerProperty, TProperty> conditionViewModel in Items)
conditionViewModel.Dispose();
Items.Clear();
}
#endregion
}
}
@@ -1,66 +0,0 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.ConditionalDataBinding.DataBindingConditionView"
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"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Border BorderThickness="0 0 0 1"
BorderBrush="{DynamicResource MaterialDesignDivider}"
Margin="0 0 0 -4"
Padding="0 4"
Background="Transparent">
<Border.ContextMenu>
<ContextMenu>
<MenuItem Header="Add new condition" Command="{s:Action AddCondition}">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="CodeNotEqual" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Duplicate" Command="{s:Action DuplicateCondition}" InputGestureText="Ctrl+D" IsEnabled="False">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="ContentDuplicate" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Copy" Command="{s:Action CopyCondition}" InputGestureText="Ctrl+C" IsEnabled="False">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="ContentCopy" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Paste" Command="{s:Action PasteCondition}" InputGestureText="Ctrl+V" IsEnabled="False">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="ContentPaste" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Delete" Command="{s:Action RemoveCondition}" InputGestureText="Del">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="TrashCan" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</Border.ContextMenu>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ContentControl Grid.Row="0"
s:View.Model="{Binding ActiveItem}"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsTabStop="False"
Margin="0 -10 0 0"/>
<ContentControl Grid.Row="1"
Margin="26 0 0 0"
s:View.Model="{Binding ValueViewModel}"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsTabStop="False" />
</Grid>
</Border>
</UserControl>
@@ -1,86 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.Conditions;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Input;
using Artemis.UI.Shared.Services;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.ConditionalDataBinding
{
public sealed class DataBindingConditionViewModel<TLayerProperty, TProperty> : Conductor<DataModelConditionGroupViewModel>, IDisposable
{
private readonly IDataModelConditionsVmFactory _dataModelConditionsVmFactory;
private readonly IDataModelUIService _dataModelUIService;
private readonly IProfileEditorService _profileEditorService;
public DataBindingConditionViewModel(DataBindingCondition<TLayerProperty, TProperty> dataBindingCondition,
IProfileEditorService profileEditorService,
IDataModelConditionsVmFactory dataModelConditionsVmFactory,
IDataModelUIService dataModelUIService)
{
_profileEditorService = profileEditorService;
_dataModelConditionsVmFactory = dataModelConditionsVmFactory;
_dataModelUIService = dataModelUIService;
DataBindingCondition = dataBindingCondition;
}
public DataBindingCondition<TLayerProperty, TProperty> DataBindingCondition { get; }
public DataModelStaticViewModel ValueViewModel { get; set; }
protected override void OnInitialActivate()
{
base.OnInitialActivate();
List<Module> modules = new();
if (_profileEditorService.SelectedProfileConfiguration?.Module != null)
modules.Add(_profileEditorService.SelectedProfileConfiguration.Module);
ActiveItem = _dataModelConditionsVmFactory.DataModelConditionGroupViewModel(DataBindingCondition.Condition, ConditionGroupType.General, modules);
ActiveItem.IsRootGroup = true;
ActiveItem.Update();
ActiveItem.Updated += ActiveItemOnUpdated;
ValueViewModel = _dataModelUIService.GetStaticInputViewModel(typeof(TProperty), null);
ValueViewModel.ValueUpdated += ValueViewModelOnValueUpdated;
ValueViewModel.Value = DataBindingCondition.Value;
}
private void ActiveItemOnUpdated(object sender, EventArgs e)
{
if (!ActiveItem.GetChildren().Any())
DataBindingCondition.ConditionalDataBinding.RemoveCondition(DataBindingCondition);
}
private void ValueViewModelOnValueUpdated(object sender, DataModelInputStaticEventArgs e)
{
DataBindingCondition.Value = (TProperty) Convert.ChangeType(e.Value, typeof(TProperty));
_profileEditorService.SaveSelectedProfileElement();
}
public void AddCondition()
{
((ConditionalDataBindingModeViewModel<TLayerProperty, TProperty>) Parent).AddCondition();
}
public void RemoveCondition()
{
((ConditionalDataBindingModeViewModel<TLayerProperty, TProperty>)Parent).RemoveCondition(DataBindingCondition);
}
#region IDisposable
/// <inheritdoc />
public void Dispose()
{
ValueViewModel?.Dispose();
}
#endregion
}
}
@@ -1,179 +0,0 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DataBindingView"
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:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DataModelConditions.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.50*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<materialDesign:Card Grid.Column="0"
UniformCornerRadius="0"
materialDesign:ShadowAssist.ShadowDepth="Depth3"
materialDesign:ShadowAssist.ShadowEdges="Right"
Background="{DynamicResource MaterialDesignPaper}"
Panel.ZIndex="2">
<Grid Margin="10 5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ComboBox Grid.Row="0"
Style="{StaticResource MaterialDesignFilledComboBox}"
Margin="0 5"
materialDesign:HintAssist.Hint="Data binding mode"
MinWidth="128"
SelectedValue="{Binding SelectedDataBindingMode}"
ItemsSource="{Binding DataBindingModes}"
SelectedValuePath="Value"
DisplayMemberPath="Description" />
<TextBox Grid.Row="1"
Style="{StaticResource MaterialDesignFilledTextBox}"
Margin="0 5"
Text="{Binding EasingTime}"
materialDesign:TextFieldAssist.HasClearButton="True"
materialDesign:TextFieldAssist.SuffixText="ms"
materialDesign:HintAssist.Hint="Easing time"
IsEnabled="{Binding IsDataBindingEnabled}" />
<ComboBox Grid.Row="2"
Style="{StaticResource MaterialDesignFilledComboBox}"
Margin="0 5"
materialDesign:HintAssist.Hint="Easing type"
MinWidth="128"
IsEnabled="{Binding IsEasingTimeEnabled}"
SelectedItem="{Binding SelectedEasingViewModel}"
ItemsSource="{Binding EasingViewModels}">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Polyline Stroke="{DynamicResource MaterialDesignBody}"
StrokeThickness="1"
Points="{Binding EasingPoints}"
Stretch="Uniform"
Width="14"
Height="14"
Margin="0 0 10 0" />
<TextBlock Text="{Binding Description}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<materialDesign:Card Grid.Row="3" Grid.ColumnSpan="2" Margin="0 5 0 0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Margin="10 10 0 0">
Result
</TextBlock>
<TextBlock Grid.Row="0"
Grid.Column="1"
Margin="0 10 10 0"
Visibility="{Binding AlwaysApplyDataBindings.Value, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}}"
ToolTip="Check 'Apply data bindings in editor'"
Cursor="Help">
Other bindings not updating?
</TextBlock>
<Separator Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Style="{StaticResource MaterialDesignLightSeparator}" Margin="0" />
<Grid Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Margin="10 4 10 10">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Margin="0 2" Visibility="{Binding ActiveItem.SupportsTestValue, Converter={x:Static s:BoolToVisibilityConverter.Instance}}">Input</TextBlock>
<ContentControl Grid.Row="0"
Grid.Column="1"
Visibility="{Binding ActiveItem.SupportsTestValue, Converter={x:Static s:BoolToVisibilityConverter.Instance}}"
s:View.Model="{Binding TestInputValue}"
Margin="0 2"
FontFamily="Consolas"
VerticalAlignment="Stretch"
HorizontalAlignment="Right" />
<TextBlock Grid.Row="1" Grid.Column="0" Margin="0 2">Output</TextBlock>
<ContentControl Grid.Row="1"
Grid.Column="1"
s:View.Model="{Binding TestResultValue}"
Margin="0 2"
FontFamily="Consolas"
VerticalAlignment="Stretch"
HorizontalAlignment="Right" />
</Grid>
</Grid>
</materialDesign:Card>
<Grid Grid.Row="4" VerticalAlignment="Bottom" Margin="0 5">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
Style="{StaticResource MaterialDesignRaisedAccentButton}"
Margin="0 0 5 0"
ToolTip="Copy the entire data binding"
Command="{s:Action CopyDataBinding}">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="ContentCopy" />
<TextBlock Text="COPY" Margin="8 0 0 0" />
</StackPanel>
</Button>
<Button Grid.Column="1"
Style="{StaticResource MaterialDesignRaisedAccentButton}"
Margin="5 0 0 0"
ToolTip="Paste the entire data binding"
Command="{s:Action PasteDataBinding}">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="ContentPaste" />
<TextBlock Text="PASTE" Margin="8 0 0 0" />
</StackPanel>
</Button>
</Grid>
</Grid>
</materialDesign:Card>
<materialDesign:Card Grid.Column="1" UniformCornerRadius="0" Background="{DynamicResource MaterialDesignToolBarBackground}" Panel.ZIndex="1">
<Grid Margin="10 5">
<ContentControl s:View.Model="{Binding ActiveItem}"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsTabStop="False" />
</Grid>
</materialDesign:Card>
</Grid>
</UserControl>
@@ -1,15 +0,0 @@
using System.Windows.Controls;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
{
/// <summary>
/// Interaction logic for DataBindingView.xaml
/// </summary>
public partial class DataBindingView : UserControl
{
public DataBindingView()
{
InitializeComponent();
}
}
}
@@ -1,314 +0,0 @@
using System;
using System.Linq;
using System.Timers;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.Storage.Entities.Profile.DataBindings;
using Artemis.UI.Exceptions;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Stylet;
using EnumUtilities = Artemis.UI.Shared.EnumUtilities;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
{
public sealed class DataBindingViewModel<TLayerProperty, TProperty> : Conductor<IDataBindingModeViewModel>, IDataBindingViewModel
{
private readonly IDataBindingsVmFactory _dataBindingsVmFactory;
private readonly ICoreService _coreService;
private readonly IProfileEditorService _profileEditorService;
private int _easingTime;
private bool _isDataBindingEnabled;
private bool _isEasingTimeEnabled;
private DataBindingModeType _selectedDataBindingMode;
private TimelineEasingViewModel _selectedEasingViewModel;
private bool _updating;
private bool _updatingTestResult;
private DateTime _lastUpdate;
public DataBindingViewModel(DataBindingRegistration<TLayerProperty, TProperty> registration,
ICoreService coreService,
ISettingsService settingsService,
IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService,
IDataBindingsVmFactory dataBindingsVmFactory)
{
Registration = registration;
_coreService = coreService;
_profileEditorService = profileEditorService;
_dataBindingsVmFactory = dataBindingsVmFactory;
DisplayName = Registration.DisplayName.ToUpper();
AlwaysApplyDataBindings = settingsService.GetSetting("ProfileEditor.AlwaysApplyDataBindings", true);
DataBindingModes = new BindableCollection<ValueDescription>(EnumUtilities.GetAllValuesAndDescriptions(typeof(DataBindingModeType)));
EasingViewModels = new BindableCollection<TimelineEasingViewModel>();
TestInputValue = dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), null, true);
TestResultValue = dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), null, true);
}
public DataBindingRegistration<TLayerProperty, TProperty> Registration { get; }
public PluginSetting<bool> AlwaysApplyDataBindings { get; }
public BindableCollection<ValueDescription> DataBindingModes { get; }
public BindableCollection<TimelineEasingViewModel> EasingViewModels { get; }
public DataModelDisplayViewModel TestInputValue { get; }
public DataModelDisplayViewModel TestResultValue { get; }
public DataBindingModeType SelectedDataBindingMode
{
get => _selectedDataBindingMode;
set
{
if (!SetAndNotify(ref _selectedDataBindingMode, value)) return;
ApplyDataBindingMode();
Update();
}
}
public bool IsDataBindingEnabled
{
get => _isDataBindingEnabled;
set => SetAndNotify(ref _isDataBindingEnabled, value);
}
public TimelineEasingViewModel SelectedEasingViewModel
{
get => _selectedEasingViewModel;
set
{
if (!SetAndNotify(ref _selectedEasingViewModel, value)) return;
ApplyChanges();
}
}
public int EasingTime
{
get => _easingTime;
set
{
if (!SetAndNotify(ref _easingTime, value)) return;
ApplyChanges();
}
}
public bool IsEasingTimeEnabled
{
get => _isEasingTimeEnabled;
set
{
if (!SetAndNotify(ref _isEasingTimeEnabled, value)) return;
ApplyChanges();
}
}
protected override void OnInitialActivate()
{
Initialize();
base.OnInitialActivate();
}
private void Initialize()
{
EasingViewModels.AddRange(Enum.GetValues(typeof(Easings.Functions)).Cast<Easings.Functions>().Select(v => new TimelineEasingViewModel(v, false)));
_lastUpdate = DateTime.Now;
_coreService.FrameRendered += OnFrameRendered;
CreateDataBindingModeModeViewModel();
Update();
}
private void CreateDataBindingModeModeViewModel()
{
if (Registration.DataBinding?.DataBindingMode == null)
{
ActiveItem = null;
return;
}
switch (Registration.DataBinding.DataBindingMode)
{
case DirectDataBinding<TLayerProperty, TProperty> directDataBinding:
ActiveItem = _dataBindingsVmFactory.DirectDataBindingModeViewModel(directDataBinding);
break;
case ConditionalDataBinding<TLayerProperty, TProperty> conditionalDataBinding:
ActiveItem = _dataBindingsVmFactory.ConditionalDataBindingModeViewModel(conditionalDataBinding);
break;
}
}
private void Update()
{
if (_updating)
return;
if (Registration.DataBinding == null)
{
IsDataBindingEnabled = false;
IsEasingTimeEnabled = false;
return;
}
_updating = true;
IsDataBindingEnabled = ActiveItem != null;
EasingTime = (int) Registration.DataBinding.EasingTime.TotalMilliseconds;
SelectedEasingViewModel = EasingViewModels.First(vm => vm.EasingFunction == Registration.DataBinding.EasingFunction);
IsEasingTimeEnabled = EasingTime > 0;
SelectedDataBindingMode = Registration.DataBinding.DataBindingMode switch
{
DirectDataBinding<TLayerProperty, TProperty> _ => DataBindingModeType.Direct,
ConditionalDataBinding<TLayerProperty, TProperty> _ => DataBindingModeType.Conditional,
_ => DataBindingModeType.None
};
ActiveItem?.Update();
_updating = false;
}
private void ApplyChanges()
{
if (_updating)
return;
if (Registration.DataBinding != null)
{
Registration.DataBinding.EasingTime = TimeSpan.FromMilliseconds(EasingTime);
Registration.DataBinding.EasingFunction = SelectedEasingViewModel?.EasingFunction ?? Easings.Functions.Linear;
}
_profileEditorService.SaveSelectedProfileElement();
Update();
}
private void ApplyDataBindingMode()
{
if (_updating)
return;
if (Registration.DataBinding != null && SelectedDataBindingMode == DataBindingModeType.None)
RemoveDataBinding();
else
{
if (Registration.DataBinding == null && SelectedDataBindingMode != DataBindingModeType.None)
EnableDataBinding();
Registration.DataBinding!.ChangeDataBindingMode(SelectedDataBindingMode);
}
CreateDataBindingModeModeViewModel();
_profileEditorService.SaveSelectedProfileElement();
}
private void UpdateTestResult()
{
if (_updating || _updatingTestResult)
return;
_updatingTestResult = true;
if (Registration.DataBinding == null || ActiveItem == null)
{
TestInputValue.UpdateValue(default);
TestResultValue.UpdateValue(default);
_updatingTestResult = false;
return;
}
// If always update is disabled, do constantly update the data binding as long as the view model is open
// If always update is enabled, this is done for all data bindings in ProfileViewModel
if (!AlwaysApplyDataBindings.Value)
{
Registration.DataBinding.UpdateWithDelta(DateTime.Now - _lastUpdate);
_profileEditorService.UpdateProfilePreview();
}
if (ActiveItem.SupportsTestValue)
{
TProperty currentValue = Registration.Converter.ConvertFromObject(ActiveItem?.GetTestValue() ?? default(TProperty));
TestInputValue.UpdateValue(currentValue);
TestResultValue.UpdateValue(Registration.DataBinding != null ? Registration.DataBinding.GetValue(currentValue) : default);
}
else
{
TestResultValue.UpdateValue(Registration.DataBinding != null ? Registration.DataBinding.GetValue(default) : default);
}
_updatingTestResult = false;
}
private void EnableDataBinding()
{
if (Registration.DataBinding != null)
return;
Registration.LayerProperty.EnableDataBinding(Registration);
_profileEditorService.SaveSelectedProfileElement();
}
private void RemoveDataBinding()
{
if (Registration.DataBinding == null)
return;
ActiveItem = null;
Registration.LayerProperty.DisableDataBinding(Registration.DataBinding);
Update();
_profileEditorService.SaveSelectedProfileElement();
}
public void CopyDataBinding()
{
if (Registration.DataBinding != null)
JsonClipboard.SetObject(Registration.DataBinding.Entity);
}
public void PasteDataBinding()
{
if (Registration.DataBinding == null)
Registration.LayerProperty.EnableDataBinding(Registration);
if (Registration.DataBinding == null)
throw new ArtemisUIException("Failed to create a data binding in order to paste");
DataBindingEntity dataBindingEntity = JsonClipboard.GetData<DataBindingEntity>();
if (dataBindingEntity == null)
return;
Registration.DataBinding.EasingTime = dataBindingEntity.EasingTime;
Registration.DataBinding.EasingFunction = (Easings.Functions) dataBindingEntity.EasingFunction;
Registration.DataBinding.ApplyDataBindingEntity(dataBindingEntity.DataBindingMode);
CreateDataBindingModeModeViewModel();
Update();
_profileEditorService.SaveSelectedProfileElement();
}
private void OnFrameRendered(object sender, FrameRenderedEventArgs e)
{
UpdateTestResult();
_lastUpdate = DateTime.Now;
}
private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
{
UpdateTestResult();
}
#region IDisposable
/// <inheritdoc />
public void Dispose()
{
_coreService.FrameRendered -= OnFrameRendered;
}
#endregion
}
public interface IDataBindingViewModel : IScreen, IDisposable
{
}
}
@@ -4,20 +4,51 @@
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:controls="clr-namespace:Artemis.VisualScripting.Editor.Controls;assembly=Artemis.VisualScripting"
xmlns:layerProperties="clr-namespace:Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<TabControl ItemsSource="{Binding Items}"
SelectedIndex="{Binding SelectedItemIndex}"
Style="{StaticResource MaterialDesignTabControl}" >
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl s:View.Model="{Binding}" TextElement.Foreground="{DynamicResource MaterialDesignBody}" />
</DataTemplate>
</TabControl.ContentTemplate>
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Margin="-12 0" Text="{Binding DisplayName}"></TextBlock>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance {x:Type layerProperties:DataBindingsViewModel}}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="48" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}"
VerticalAlignment="Center"
Margin="15 0"
Text="{Binding DataBinding.BaseLayerProperty.PropertyDescription.Name}" />
<CheckBox Grid.Column="1"
VerticalAlignment="Center"
IsChecked="{Binding DataBindingEnabled}">
Enable data binding
</CheckBox>
</Grid>
<controls:VisualScriptPresenter Grid.Row="1"
AvailableNodes="{Binding AvailableNodes}"
Script="{Binding DataBinding.Script}"
AlwaysShowValues="{Binding AlwaysShowValues.Value}"/>
<Border Grid.Row="1" Background="{StaticResource MaterialDesignToolBarBackground}"
Visibility="{Binding DataBindingEnabled, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Margin="16">
<materialDesign:PackIcon Kind="TransitConnectionVariant" Width="100" Height="100" HorizontalAlignment="Center" />
<TextBlock Style="{StaticResource MaterialDesignHeadline4TextBlock}" TextWrapping="Wrap" HorizontalAlignment="Center" Margin="0 25">
Enable this data binding to use nodes!
</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}" TextWrapping="Wrap" HorizontalAlignment="Center" TextAlignment="Center">
When you enable data bindings you can no longer use keyframes or normal values for this property. <LineBreak />
Instead you'll be using our node scripting system to set a value based on the data model or values of other layers/folders.
</TextBlock>
</StackPanel>
</Border>
</Grid>
</UserControl>
@@ -1,88 +1,71 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.UI.Ninject.Factories;
using Artemis.Core.Services;
using Artemis.UI.Shared.Services;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
{
public class DataBindingsViewModel : Conductor<IDataBindingViewModel>.Collection.AllActive
public class DataBindingsViewModel : Screen
{
private readonly IDataBindingsVmFactory _dataBindingsVmFactory;
private readonly IProfileEditorService _profileEditorService;
private ILayerProperty _selectedDataBinding;
private int _selectedItemIndex;
private bool _updating;
private IDataBinding _dataBinding;
public DataBindingsViewModel(IProfileEditorService profileEditorService, IDataBindingsVmFactory dataBindingsVmFactory)
public DataBindingsViewModel(IProfileEditorService profileEditorService, INodeService nodeService, ISettingsService settingsService)
{
_profileEditorService = profileEditorService;
_dataBindingsVmFactory = dataBindingsVmFactory;
AvailableNodes = nodeService.AvailableNodes.ToList();
AlwaysShowValues = settingsService.GetSetting("ProfileEditor.AlwaysShowValues", true);
}
public int SelectedItemIndex
public List<NodeData> AvailableNodes { get; }
public PluginSetting<bool> AlwaysShowValues { get; }
public IDataBinding DataBinding
{
get => _selectedItemIndex;
set => SetAndNotify(ref _selectedItemIndex, value);
get => _dataBinding;
set => SetAndNotify(ref _dataBinding, value);
}
private void CreateDataBindingViewModels()
public bool DataBindingEnabled
{
int oldIndex = SelectedItemIndex;
Items.Clear();
ILayerProperty layerProperty = _profileEditorService.SelectedDataBinding;
if (layerProperty == null)
return;
List<IDataBindingRegistration> registrations = layerProperty.GetAllDataBindingRegistrations();
// Create a data binding VM for each data bindable property. These VMs will be responsible for retrieving
// and creating the actual data bindings
Items.AddRange(registrations.Select(registration => _dataBindingsVmFactory.DataBindingViewModel(registration)));
SelectedItemIndex = Items.Count < oldIndex ? 0 : oldIndex;
get => _dataBinding?.IsEnabled ?? false;
set
{
if (_dataBinding != null)
_dataBinding.IsEnabled = value;
}
}
private void ProfileEditorServiceOnSelectedDataBindingChanged(object sender, EventArgs e)
{
CreateDataBindingViewModels();
SubscribeToSelectedDataBinding();
SelectedItemIndex = 0;
}
private void SubscribeToSelectedDataBinding()
{
if (_selectedDataBinding != null)
if (DataBinding != null)
{
_selectedDataBinding.DataBindingPropertyRegistered -= DataBindingRegistrationsChanged;
_selectedDataBinding.DataBindingPropertiesCleared -= DataBindingRegistrationsChanged;
DataBinding.DataBindingEnabled -= DataBindingOnDataBindingToggled;
DataBinding.DataBindingDisabled -= DataBindingOnDataBindingToggled;
}
_selectedDataBinding = _profileEditorService.SelectedDataBinding;
if (_selectedDataBinding != null)
DataBinding = _profileEditorService.SelectedDataBinding;
if (DataBinding != null)
{
_selectedDataBinding.DataBindingPropertyRegistered += DataBindingRegistrationsChanged;
_selectedDataBinding.DataBindingPropertiesCleared += DataBindingRegistrationsChanged;
DataBinding.DataBindingEnabled += DataBindingOnDataBindingToggled;
DataBinding.DataBindingDisabled += DataBindingOnDataBindingToggled;
OnPropertyChanged(nameof(DataBindingEnabled));
}
}
private void DataBindingRegistrationsChanged(object sender, LayerPropertyEventArgs e)
private void DataBindingOnDataBindingToggled(object sender, DataBindingEventArgs e)
{
if (_updating)
return;
_updating = true;
Execute.PostToUIThread(async () =>
{
await Task.Delay(200);
CreateDataBindingViewModels();
_updating = false;
});
OnPropertyChanged(nameof(DataBindingEnabled));
}
#region Overrides of Screen
@@ -90,17 +73,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
protected override void OnInitialActivate()
{
_profileEditorService.SelectedDataBindingChanged += ProfileEditorServiceOnSelectedDataBindingChanged;
CreateDataBindingViewModels();
SubscribeToSelectedDataBinding();
base.OnInitialActivate();
}
protected override void OnActivate()
{
SelectedItemIndex = 0;
base.OnActivate();
}
protected override void OnClose()
{
_profileEditorService.SelectedDataBindingChanged -= ProfileEditorServiceOnSelectedDataBindingChanged;
@@ -1,97 +0,0 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDataBinding.DataBindingModifierView"
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:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDataBinding"
xmlns:modifierTypes="clr-namespace:Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDataBinding.ModifierTypes"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DataModelConditions.xaml" />
<ResourceDictionary>
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<utilities:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
ToolTip="Delete the modifier"
Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Left"
Foreground="#E74C4C"
Width="25"
Height="25"
Command="{s:Action Delete}">
<materialDesign:PackIcon Kind="Close" Width="18" Height="18" />
</Button>
<Button Grid.Column="1"
Style="{StaticResource DataModelConditionButtonLeftClickMenu}"
Background="#7B7B7B"
BorderBrush="#7B7B7B"
Click="PropertyButton_OnClick">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding ModifierTypeViewModels.Children}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<StackPanel>
<StackPanel.Resources>
<DataTemplate DataType="{x:Type modifierTypes:ModifierTypeCategoryViewModel}">
<TextBlock Text="{Binding Category}" />
</DataTemplate>
<DataTemplate DataType="{x:Type modifierTypes:ModifierTypeViewModel}">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="{Binding ModifierType.Icon}"
VerticalAlignment="Center"
Margin="0 0 15 0"
Visibility="{Binding ModifierType.Icon, Converter={StaticResource NullToVisibilityConverter}}"/>
<TextBlock Text="{Binding ModifierType.Name}"
VerticalAlignment="Center"
ToolTip="{Binding ModifierType.Description}"/>
</StackPanel>
</DataTemplate>
</StackPanel.Resources>
<ContentPresenter Content="{Binding}" />
</StackPanel>
</DataTemplate>
</ContextMenu.ItemTemplate>
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MaterialDesignMenuItem}">
<Setter Property="ItemsSource" Value="{Binding Children}" />
<Setter Property="Command" Value="{Binding Data.SelectModifierTypeCommand, Source={StaticResource DataContextProxy}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
<Setter Property="CommandTarget" Value="{Binding}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
<Grid>
<TextBlock Text="{Binding SelectedModifierType.Name}"
Visibility="{Binding SelectedModifierType, Converter={StaticResource NullToVisibilityConverter}}" />
<TextBlock FontStyle="Italic"
Visibility="{Binding SelectedModifierType, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}">
« Select a modifier »
</TextBlock>
</Grid>
</Button>
<ContentControl Grid.Column="2" s:View.Model="{Binding DynamicSelectionViewModel}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
<ContentControl Grid.Column="2" s:View.Model="{Binding StaticInputViewModel}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
</Grid>
</UserControl>
@@ -1,26 +0,0 @@
using System.Windows;
using System.Windows.Controls;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDataBinding
{
/// <summary>
/// Interaction logic for DataBindingModifierView.xaml
/// </summary>
public partial class DataBindingModifierView : UserControl
{
public DataBindingModifierView()
{
InitializeComponent();
}
private void PropertyButton_OnClick(object sender, RoutedEventArgs e)
{
// DataContext is not set when using left button, I don't know why but there it is
if (sender is Button button && button.ContextMenu != null)
{
button.ContextMenu.DataContext = button.DataContext;
button.ContextMenu.IsOpen = true;
}
}
}
}
@@ -1,212 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Exceptions;
using Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDataBinding.ModifierTypes;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Input;
using Artemis.UI.Shared.Services;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDataBinding
{
public sealed class DataBindingModifierViewModel<TLayerProperty, TProperty> : PropertyChangedBase, IDisposable
{
private readonly IDataBindingService _dataBindingService;
private readonly IDataModelUIService _dataModelUIService;
private readonly IProfileEditorService _profileEditorService;
private DataModelDynamicViewModel _dynamicSelectionViewModel;
private ModifierTypeCategoryViewModel _modifierTypeViewModels;
private BaseDataBindingModifierType _selectedModifierType;
private DataModelStaticViewModel _staticInputViewModel;
public DataBindingModifierViewModel(DataBindingModifier<TLayerProperty, TProperty> modifier,
IDataBindingService dataBindingService,
ISettingsService settingsService,
IDataModelUIService dataModelUIService,
IProfileEditorService profileEditorService)
{
_dataBindingService = dataBindingService;
_dataModelUIService = dataModelUIService;
_profileEditorService = profileEditorService;
ShowDataModelValues = settingsService.GetSetting<bool>("ProfileEditor.ShowDataModelValues");
Modifier = modifier;
SelectModifierTypeCommand = new DelegateCommand(ExecuteSelectModifierTypeCommand);
Update();
}
public DelegateCommand SelectModifierTypeCommand { get; }
public PluginSetting<bool> ShowDataModelValues { get; }
public DataBindingModifier<TLayerProperty, TProperty> Modifier { get; }
public ModifierTypeCategoryViewModel ModifierTypeViewModels
{
get => _modifierTypeViewModels;
set => SetAndNotify(ref _modifierTypeViewModels, value);
}
public BaseDataBindingModifierType SelectedModifierType
{
get => _selectedModifierType;
set => SetAndNotify(ref _selectedModifierType, value);
}
public DataModelDynamicViewModel DynamicSelectionViewModel
{
get => _dynamicSelectionViewModel;
private set => SetAndNotify(ref _dynamicSelectionViewModel, value);
}
public DataModelStaticViewModel StaticInputViewModel
{
get => _staticInputViewModel;
private set => SetAndNotify(ref _staticInputViewModel, value);
}
public void Delete()
{
Modifier.DirectDataBinding.RemoveModifier(Modifier);
_profileEditorService.SaveSelectedProfileElement();
}
private void ParameterSelectionViewModelOnPropertySelected(object sender, DataModelInputDynamicEventArgs e)
{
Modifier.UpdateParameterDynamic(e.DataModelPath);
_profileEditorService.SaveSelectedProfileElement();
}
private void StaticInputViewModelOnValueUpdated(object sender, DataModelInputStaticEventArgs e)
{
Modifier.UpdateParameterStatic(e.Value);
_profileEditorService.SaveSelectedProfileElement();
}
private void Update()
{
Type sourceType = Modifier.DirectDataBinding.GetSourceType();
if (sourceType == null)
throw new ArtemisUIException("Cannot use a data binding modifier VM for a data binding without a source");
if (Modifier.ModifierType == null || Modifier.ModifierType.ParameterType == null)
{
DisposeDynamicSelectionViewModel();
DisposeStaticInputViewModel();
}
else if (Modifier.ParameterType == ProfileRightSideType.Dynamic)
{
DisposeStaticInputViewModel();
DynamicSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.SelectedProfileConfiguration.Module);
if (DynamicSelectionViewModel != null)
{
DynamicSelectionViewModel.DisplaySwitchButton = true;
DynamicSelectionViewModel.PropertySelected += ParameterSelectionViewModelOnPropertySelected;
DynamicSelectionViewModel.SwitchToStaticRequested += DynamicSelectionViewModelOnSwitchToStaticRequested;
DynamicSelectionViewModel.FilterTypes = new[] {Modifier.ModifierType.ParameterType ?? sourceType};
}
}
else
{
DisposeDynamicSelectionViewModel();
StaticInputViewModel = _dataModelUIService.GetStaticInputViewModel(Modifier.ModifierType.ParameterType ?? sourceType, null);
if (StaticInputViewModel != null)
{
StaticInputViewModel.DisplaySwitchButton = true;
StaticInputViewModel.ValueUpdated += StaticInputViewModelOnValueUpdated;
StaticInputViewModel.SwitchToDynamicRequested += StaticInputViewModelOnSwitchToDynamicRequested;
}
}
// Modifier type
ModifierTypeCategoryViewModel root = new(null, null);
IEnumerable<IGrouping<string, BaseDataBindingModifierType>> modifierTypes = _dataBindingService.GetCompatibleModifierTypes(sourceType, ModifierTypePart.Value).GroupBy(t => t.Category);
foreach (IGrouping<string, BaseDataBindingModifierType> dataBindingModifierTypes in modifierTypes)
{
IEnumerable<ModifierTypeViewModel> viewModels = dataBindingModifierTypes.Select(t => new ModifierTypeViewModel(t));
if (dataBindingModifierTypes.Key == null)
root.Children.AddRange(viewModels);
else
root.Children.Add(new ModifierTypeCategoryViewModel(dataBindingModifierTypes.Key, viewModels));
}
ModifierTypeViewModels = root;
SelectedModifierType = Modifier.ModifierType;
// Parameter
if (DynamicSelectionViewModel != null)
DynamicSelectionViewModel.ChangeDataModelPath(Modifier.ParameterPath);
else if (StaticInputViewModel != null)
StaticInputViewModel.Value = Modifier.ParameterStaticValue;
}
private void ExecuteSelectModifierTypeCommand(object context)
{
if (!(context is ModifierTypeViewModel modifierTypeViewModel))
return;
Modifier.UpdateModifierType(modifierTypeViewModel.ModifierType);
_profileEditorService.SaveSelectedProfileElement();
Update();
}
#region IDisposable
/// <inheritdoc />
public void Dispose()
{
DisposeDynamicSelectionViewModel();
DisposeStaticInputViewModel();
}
private void DisposeStaticInputViewModel()
{
if (StaticInputViewModel != null)
{
StaticInputViewModel.Dispose();
StaticInputViewModel.ValueUpdated -= StaticInputViewModelOnValueUpdated;
StaticInputViewModel.SwitchToDynamicRequested -= StaticInputViewModelOnSwitchToDynamicRequested;
StaticInputViewModel = null;
}
}
private void DisposeDynamicSelectionViewModel()
{
if (DynamicSelectionViewModel != null)
{
DynamicSelectionViewModel.Dispose();
DynamicSelectionViewModel.PropertySelected -= ParameterSelectionViewModelOnPropertySelected;
DynamicSelectionViewModel.SwitchToStaticRequested -= DynamicSelectionViewModelOnSwitchToStaticRequested;
DynamicSelectionViewModel = null;
}
}
#endregion
#region Event handlers
private void DynamicSelectionViewModelOnSwitchToStaticRequested(object sender, EventArgs e)
{
Modifier.ParameterType = ProfileRightSideType.Static;
// Ensure the right static value is never null when the preferred type is a value type
if (SelectedModifierType.ParameterType != null &&
SelectedModifierType.ParameterType.IsValueType && Modifier.ParameterStaticValue == null)
Modifier.UpdateParameterStatic(SelectedModifierType.ParameterType.GetDefault());
Update();
}
private void StaticInputViewModelOnSwitchToDynamicRequested(object sender, EventArgs e)
{
Modifier.ParameterType = ProfileRightSideType.Dynamic;
Update();
}
#endregion
}
}
@@ -1,55 +0,0 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDataBinding.DirectDataBindingModeView"
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:dd="urn:gong-wpf-dragdrop"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Margin="0 5 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ContentControl Grid.Row="0"
s:View.Model="{Binding TargetSelectionViewModel}"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsTabStop="False" />
<Button Grid.Row="0"
Grid.Column="1"
Style="{StaticResource MaterialDesignFlatMidBgButton}"
HorizontalAlignment="Right"
FontSize="12"
Padding="6 4"
Height="22"
Command="{s:Action AddModifier}">
ADD MODIFIER
</Button>
<ListBox Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
ItemsSource="{Binding ModifierViewModels}"
materialDesign:RippleAssist.IsDisabled="True"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultDragAdorner="True"
HorizontalContentAlignment="Stretch"
Margin="0 5 0 0">
<ListBox.ItemTemplate>
<DataTemplate>
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
@@ -1,169 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.UI.Extensions;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Input;
using Artemis.UI.Shared.Services;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDataBinding
{
public sealed class DirectDataBindingModeViewModel<TLayerProperty, TProperty> : Screen, IDataBindingModeViewModel
{
private readonly IDataBindingsVmFactory _dataBindingsVmFactory;
private readonly IDataModelUIService _dataModelUIService;
private readonly IProfileEditorService _profileEditorService;
private bool _canAddModifier;
private DataModelDynamicViewModel _targetSelectionViewModel;
private bool _updating;
public DirectDataBindingModeViewModel(DirectDataBinding<TLayerProperty, TProperty> directDataBinding,
IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService,
IDataBindingsVmFactory dataBindingsVmFactory)
{
_profileEditorService = profileEditorService;
_dataModelUIService = dataModelUIService;
_dataBindingsVmFactory = dataBindingsVmFactory;
DirectDataBinding = directDataBinding;
ModifierViewModels = new BindableCollection<DataBindingModifierViewModel<TLayerProperty, TProperty>>();
Initialize();
}
public DirectDataBinding<TLayerProperty, TProperty> DirectDataBinding { get; }
public BindableCollection<DataBindingModifierViewModel<TLayerProperty, TProperty>> ModifierViewModels { get; }
public bool CanAddModifier
{
get => _canAddModifier;
set => SetAndNotify(ref _canAddModifier, value);
}
public DataModelDynamicViewModel TargetSelectionViewModel
{
get => _targetSelectionViewModel;
private set => SetAndNotify(ref _targetSelectionViewModel, value);
}
public bool SupportsTestValue => true;
public void Update()
{
TargetSelectionViewModel.ChangeDataModelPath(DirectDataBinding.SourcePath);
TargetSelectionViewModel.FilterTypes = new[] {DirectDataBinding.DataBinding.GetTargetType()};
CanAddModifier = DirectDataBinding.SourcePath != null && DirectDataBinding.SourcePath.IsValid;
UpdateModifierViewModels();
}
public object GetTestValue()
{
return DirectDataBinding.SourcePath?.GetValue();
}
#region IDisposable
/// <inheritdoc />
public void Dispose()
{
TargetSelectionViewModel.PropertySelected -= TargetSelectionViewModelOnPropertySelected;
TargetSelectionViewModel.Dispose();
DirectDataBinding.ModifiersUpdated -= DirectDataBindingOnModifiersUpdated;
foreach (DataBindingModifierViewModel<TLayerProperty, TProperty> dataBindingModifierViewModel in ModifierViewModels)
dataBindingModifierViewModel.Dispose();
ModifierViewModels.Clear();
}
#endregion
private void Initialize()
{
DirectDataBinding.ModifiersUpdated += DirectDataBindingOnModifiersUpdated;
TargetSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.SelectedProfileConfiguration.Module);
TargetSelectionViewModel.PropertySelected += TargetSelectionViewModelOnPropertySelected;
ModifierViewModels.CollectionChanged += ModifierViewModelsOnCollectionChanged;
Update();
}
private void ModifierViewModelsOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (_updating || e.Action != NotifyCollectionChangedAction.Add)
return;
for (int index = 0; index < ModifierViewModels.Count; index++)
{
DataBindingModifierViewModel<TLayerProperty, TProperty> dataBindingModifierViewModel = ModifierViewModels[index];
dataBindingModifierViewModel.Modifier.Order = index + 1;
}
DirectDataBinding.ApplyOrder();
_profileEditorService.SaveSelectedProfileElement();
}
#region Target
private void TargetSelectionViewModelOnPropertySelected(object sender, DataModelInputDynamicEventArgs e)
{
DirectDataBinding.UpdateSource(e.DataModelPath);
Update();
_profileEditorService.SaveSelectedProfileElement();
}
#endregion
#region Modifiers
public void AddModifier()
{
DirectDataBinding.AddModifier(ProfileRightSideType.Dynamic);
_profileEditorService.SaveSelectedProfileElement();
}
private void UpdateModifierViewModels()
{
_updating = true;
// Remove old VMs
List<DataBindingModifierViewModel<TLayerProperty, TProperty>> toRemove = ModifierViewModels.Where(m => !DirectDataBinding.Modifiers.Contains(m.Modifier)).ToList();
foreach (DataBindingModifierViewModel<TLayerProperty, TProperty> modifierViewModel in toRemove)
{
ModifierViewModels.Remove(modifierViewModel);
modifierViewModel.Dispose();
}
// Don't create children without a source or with an invalid source
if (DirectDataBinding.SourcePath == null || !DirectDataBinding.SourcePath.IsValid)
return;
// Add missing VMs
foreach (DataBindingModifier<TLayerProperty, TProperty> modifier in DirectDataBinding.Modifiers)
{
if (ModifierViewModels.All(m => m.Modifier != modifier))
ModifierViewModels.Add(_dataBindingsVmFactory.DataBindingModifierViewModel(modifier));
}
// Fix order
ModifierViewModels.Sort(m => m.Modifier.Order);
_updating = false;
}
private void DirectDataBindingOnModifiersUpdated(object sender, EventArgs e)
{
UpdateModifierViewModels();
}
#endregion
}
}
@@ -1,6 +0,0 @@
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDataBinding.ModifierTypes
{
public interface IModifierTypeViewModel
{
}
}
@@ -1,19 +0,0 @@
using System.Collections.Generic;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDataBinding.ModifierTypes
{
public class ModifierTypeCategoryViewModel : IModifierTypeViewModel
{
public ModifierTypeCategoryViewModel(string category, IEnumerable<IModifierTypeViewModel> children)
{
Category = category;
Children = children == null
? new BindableCollection<IModifierTypeViewModel>()
: new BindableCollection<IModifierTypeViewModel>(children);
}
public string Category { get; set; }
public BindableCollection<IModifierTypeViewModel> Children { get; set; }
}
}
@@ -1,14 +0,0 @@
using Artemis.Core;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDataBinding.ModifierTypes
{
public class ModifierTypeViewModel : IModifierTypeViewModel
{
public ModifierTypeViewModel(BaseDataBindingModifierType modifierType)
{
ModifierType = modifierType;
}
public BaseDataBindingModifierType ModifierType { get; set; }
}
}
@@ -1,12 +0,0 @@
using System;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
{
public interface IDataBindingModeViewModel : IScreen, IDisposable
{
bool SupportsTestValue { get; }
void Update();
object GetTestValue();
}
}
@@ -156,7 +156,6 @@
</DockPanel>
</Border>
<!-- Properties tree -->
<materialDesign:DialogHost Identifier="PropertyTreeDialogHost" DialogTheme="Inherit" CloseOnClickAway="True" Grid.Row="1">
<materialDesign:Transitioner x:Name="PropertyTreeTransitioner" SelectedIndex="{Binding PropertyTreeIndex}" DefaultTransitionOrigin="0.9, 0.9" AutoApplyTransitionOrigins="True">
@@ -39,10 +39,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
public void ActivateDataBindingViewModel()
{
if (_profileEditorService.SelectedDataBinding == LayerProperty)
if (_profileEditorService.SelectedDataBinding == LayerProperty.DataBinding)
_profileEditorService.ChangeSelectedDataBinding(null);
else
_profileEditorService.ChangeSelectedDataBinding(LayerProperty);
_profileEditorService.ChangeSelectedDataBinding(LayerProperty.DataBinding);
}
public void ResetToDefault()
@@ -92,8 +92,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
{
_profileEditorService.SelectedDataBindingChanged += ProfileEditorServiceOnSelectedDataBindingChanged;
LayerProperty.VisibilityChanged += LayerPropertyOnVisibilityChanged;
LayerProperty.DataBindingEnabled += LayerPropertyOnDataBindingChange;
LayerProperty.DataBindingDisabled += LayerPropertyOnDataBindingChange;
LayerProperty.DataBinding.DataBindingEnabled += LayerPropertyOnDataBindingChange;
LayerProperty.DataBinding.DataBindingDisabled += LayerPropertyOnDataBindingChange;
LayerProperty.KeyframesToggled += LayerPropertyOnKeyframesToggled;
LayerPropertyViewModel.IsVisible = !LayerProperty.IsHidden;
base.OnInitialActivate();
@@ -104,8 +104,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
{
_profileEditorService.SelectedDataBindingChanged -= ProfileEditorServiceOnSelectedDataBindingChanged;
LayerProperty.VisibilityChanged -= LayerPropertyOnVisibilityChanged;
LayerProperty.DataBindingEnabled -= LayerPropertyOnDataBindingChange;
LayerProperty.DataBindingDisabled -= LayerPropertyOnDataBindingChange;
LayerProperty.DataBinding.DataBindingEnabled -= LayerPropertyOnDataBindingChange;
LayerProperty.DataBinding.DataBindingDisabled -= LayerPropertyOnDataBindingChange;
LayerProperty.KeyframesToggled -= LayerPropertyOnKeyframesToggled;
base.OnClose();
}
@@ -116,7 +116,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
private void ProfileEditorServiceOnSelectedDataBindingChanged(object sender, EventArgs e)
{
LayerPropertyViewModel.IsHighlighted = _profileEditorService.SelectedDataBinding == LayerProperty;
LayerPropertyViewModel.IsHighlighted = _profileEditorService.SelectedDataBinding == LayerProperty.DataBinding;
}
private void LayerPropertyOnVisibilityChanged(object sender, LayerPropertyEventArgs e)
@@ -124,7 +124,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
LayerPropertyViewModel.IsVisible = !LayerProperty.IsHidden;
}
private void LayerPropertyOnDataBindingChange(object sender, LayerPropertyEventArgs e)
private void LayerPropertyOnDataBindingChange(object sender, DataBindingEventArgs e)
{
NotifyOfPropertyChange(nameof(HasDataBinding));
}
@@ -124,7 +124,11 @@
IsChecked="{Binding ShowDataModelValues.Value}"/>
<MenuItem Header="Display Full Condition Paths"
IsCheckable="True"
IsChecked="{Binding ShowFullPaths.Value}"/>
IsChecked="{Binding ShowFullPaths.Value}"/>
<MenuItem Header="Always Display Cable Values"
ToolTip="If enabled, cable values are always shown instead of only on hover"
IsCheckable="True"
IsChecked="{Binding AlwaysShowValues.Value}"/>
<MenuItem Header="Apply All Data Bindings During Edit"
ToolTip="If enabled, updates all data bindings instead of only the one you are editing"
IsCheckable="True"
@@ -119,6 +119,7 @@ namespace Artemis.UI.Screens.ProfileEditor
public PluginSetting<bool> StopOnFocusLoss => _settingsService.GetSetting("ProfileEditor.StopOnFocusLoss", true);
public PluginSetting<bool> ShowDataModelValues => _settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false);
public PluginSetting<bool> ShowFullPaths => _settingsService.GetSetting("ProfileEditor.ShowFullPaths", true);
public PluginSetting<bool> AlwaysShowValues => _settingsService.GetSetting("ProfileEditor.AlwaysShowValues", true);
public PluginSetting<bool> FocusSelectedLayer => _settingsService.GetSetting("ProfileEditor.FocusSelectedLayer", true);
public PluginSetting<bool> AlwaysApplyDataBindings => _settingsService.GetSetting("ProfileEditor.AlwaysApplyDataBindings", true);
@@ -194,6 +195,7 @@ namespace Artemis.UI.Screens.ProfileEditor
StopOnFocusLoss.AutoSave = true;
ShowDataModelValues.AutoSave = true;
ShowFullPaths.AutoSave = true;
AlwaysShowValues.AutoSave = true;
FocusSelectedLayer.AutoSave = true;
AlwaysApplyDataBindings.AutoSave = true;
@@ -208,6 +210,7 @@ namespace Artemis.UI.Screens.ProfileEditor
StopOnFocusLoss.AutoSave = false;
ShowDataModelValues.AutoSave = false;
ShowFullPaths.AutoSave = false;
AlwaysShowValues.AutoSave = false;
FocusSelectedLayer.AutoSave = false;
AlwaysApplyDataBindings.AutoSave = false;
@@ -30,7 +30,6 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
private bool _canSelectEditTool;
private BindableCollection<ArtemisDevice> _devices;
private BindableCollection<ArtemisLed> _highlightedLeds;
private DateTime _lastUpdate;
private PanZoomViewModel _panZoomViewModel;
private Layer _previousSelectedLayer;
private int _previousTool;
@@ -138,7 +137,6 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
ApplyActiveProfile();
_lastUpdate = DateTime.Now;
_coreService.FrameRendered += OnFrameRendered;
_rgbService.DeviceAdded += RgbServiceOnDevicesModified;
@@ -232,7 +230,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
if (CanSelectEditTool == false && ActiveToolIndex == 1)
ActivateToolByIndex(2);
}
#region Buttons
private void ActivateToolByIndex(int value)
@@ -313,23 +311,32 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
private void OnFrameRendered(object sender, FrameRenderedEventArgs e)
{
TimeSpan delta = DateTime.Now - _lastUpdate;
_lastUpdate = DateTime.Now;
if (SuspendedEditing || !_settingsService.GetSetting("ProfileEditor.AlwaysApplyDataBindings", true).Value || _profileEditorService.SelectedProfile == null)
if (SuspendedEditing || _profileEditorService.Playing || _profileEditorService.SelectedProfile == null)
return;
foreach (IDataBindingRegistration dataBindingRegistration in _profileEditorService.SelectedProfile.GetAllFolders()
.SelectMany(f => f.GetAllLayerProperties(), (f, p) => p)
.SelectMany(p => p.GetAllDataBindingRegistrations()))
dataBindingRegistration.GetDataBinding()?.UpdateWithDelta(delta);
foreach (IDataBindingRegistration dataBindingRegistration in _profileEditorService.SelectedProfile.GetAllLayers()
.SelectMany(f => f.GetAllLayerProperties(), (f, p) => p)
.SelectMany(p => p.GetAllDataBindingRegistrations()))
dataBindingRegistration.GetDataBinding()?.UpdateWithDelta(delta);
bool hasEnabledDataBindings = false;
// TODO: Only update when there are data bindings
if (!_profileEditorService.Playing)
// Always update the currently selected data binding
if (_profileEditorService.SelectedDataBinding != null && _profileEditorService.SelectedDataBinding.IsEnabled)
{
hasEnabledDataBindings = true;
_profileEditorService.SelectedDataBinding.BaseLayerProperty.UpdateDataBinding();
}
// Update all other data bindings if the user enabled this
if (_settingsService.GetSetting("ProfileEditor.AlwaysApplyDataBindings", true).Value)
{
foreach (ILayerProperty layerProperty in _profileEditorService.SelectedProfile.GetAllRenderElements().SelectMany(f => f.GetAllLayerProperties(), (_, p) => p))
{
if (layerProperty.BaseDataBinding != _profileEditorService.SelectedDataBinding && layerProperty.BaseDataBinding.IsEnabled)
{
hasEnabledDataBindings = true;
layerProperty.UpdateDataBinding();
}
}
}
if (hasEnabledDataBindings)
_profileEditorService.UpdateProfilePreview();
}
@@ -0,0 +1,36 @@
<mde:MaterialWindow x:Class="Artemis.UI.Screens.ProfileEditor.Windows.NodeScriptWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mde="https://spiegelp.github.io/MaterialDesignExtensions/winfx/xaml"
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
xmlns:controls="clr-namespace:Artemis.VisualScripting.Editor.Controls;assembly=Artemis.VisualScripting"
xmlns:windows="clr-namespace:Artemis.UI.Screens.ProfileEditor.Windows"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
Title="Node Script | Artemis"
TitleBarIcon="{svgc:SvgImage Source=/Resources/Images/Logo/bow-white.svg}"
Foreground="{DynamicResource MaterialDesignBody}"
Background="{DynamicResource MaterialDesignPaper}"
FontFamily="pack://application:,,,/MaterialDesignThemes.Wpf;component/Resources/Roboto/#Roboto"
UseLayoutRounding="True"
FadeContentIfInactive="False"
Width="1200"
Height="1000"
d:DesignHeight="800" d:DesignWidth="800"
d:DataContext="{d:DesignInstance windows:NodeScriptWindowViewModel}"
Icon="/Resources/Images/Logo/bow.ico">
<materialDesign:DialogHost IsTabStop="False" Focusable="False" Identifier="NodeScriptDialog" DialogTheme="Inherit">
<DockPanel>
<mde:AppBar Type="Dense" Title="Node Script Editor" DockPanel.Dock="Top" Margin="-18 0 0 0">
<mde:AppBar.AppIcon>
<materialDesign:PackIcon Kind="FreehandLine" Width="20" Height="28" />
</mde:AppBar.AppIcon>
</mde:AppBar>
<controls:VisualScriptEditor Script="{Binding NodeScript}"
AvailableNodes="{Binding AvailableNodes}"
AlwaysShowValues="{Binding AlwaysShowValues.Value}" />
</DockPanel>
</materialDesign:DialogHost>
</mde:MaterialWindow>
@@ -0,0 +1,20 @@
using Artemis.Core;
using Artemis.Core.Services;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.Windows
{
public class NodeScriptWindowViewModel : Screen
{
public NodeScriptWindowViewModel(NodeScript nodeScript, INodeService nodeService, ISettingsService settingsService)
{
NodeScript = nodeScript;
AvailableNodes = new BindableCollection<NodeData>(nodeService.AvailableNodes);
AlwaysShowValues = settingsService.GetSetting("ProfileEditor.AlwaysShowValues", true);
}
public NodeScript NodeScript { get; }
public BindableCollection<NodeData> AvailableNodes { get; }
public PluginSetting<bool> AlwaysShowValues { get; }
}
}
+10 -4
View File
@@ -32,11 +32,12 @@ namespace Artemis.UI.Screens
private readonly IWindowManager _windowManager;
private readonly PluginSetting<WindowSize> _windowSize;
private static bool _registeredBuiltInTypes;
private bool _lostFocus;
private ISnackbarMessageQueue _mainMessageQueue;
private MaterialWindow _window;
private string _windowTitle;
public RootViewModel(
IKernel kernel,
IEventAggregator eventAggregator,
@@ -169,9 +170,14 @@ namespace Artemis.UI.Screens
SidebarViewModel.SelectedScreenChanged += SidebarViewModelOnSelectedScreenChanged;
ActiveItem = SidebarViewModel.SelectedScreen;
_builtInRegistrationService.RegisterBuiltInDataModelDisplays();
_builtInRegistrationService.RegisterBuiltInDataModelInputs();
_builtInRegistrationService.RegisterBuiltInPropertyEditors();
if (!_registeredBuiltInTypes)
{
_builtInRegistrationService.RegisterBuiltInDataModelDisplays();
_builtInRegistrationService.RegisterBuiltInDataModelInputs();
_builtInRegistrationService.RegisterBuiltInPropertyEditors();
_builtInRegistrationService.RegisterBuiltInNodeTypes();
_registeredBuiltInTypes = true;
}
_window = (MaterialWindow) View;
@@ -21,15 +21,9 @@
d:DesignHeight="800" d:DesignWidth="800" d:DataContext="{d:DesignInstance debug:DebugViewModel}"
Icon="/Resources/Images/Logo/bow.ico"
Topmost="{Binding StayOnTopSetting.Value}">
<materialDesign:DialogHost IsTabStop="False"
Focusable="False"
Identifier="DebuggerDialog"
DialogTheme="Inherit">
<materialDesign:DialogHost IsTabStop="False" Focusable="False" Identifier="DebuggerDialog" DialogTheme="Inherit">
<DockPanel>
<mde:AppBar Type="Dense"
Title="Debugger"
DockPanel.Dock="Top"
Margin="-18 0 0 0">
<mde:AppBar Type="Dense" Title="Debugger" DockPanel.Dock="Top" Margin="-18 0 0 0">
<mde:AppBar.AppIcon>
<materialDesign:PackIcon Kind="Matrix" Width="20" Height="28" />
</mde:AppBar.AppIcon>
@@ -289,13 +289,6 @@
</TextBlock>
</StackPanel>
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}" LineHeight="25" Margin="10 0 0 0">
- Ben.Demystifier
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/benaadams/Ben.Demystifier">
https://github.com/benaadams/Ben.Demystifier
</Hyperlink>
<LineBreak />
- EmbedIO
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
@@ -380,6 +373,13 @@
http://www.ninject.org/
</Hyperlink>
<LineBreak />
- NoStringEvaluating
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/KovtunV/NoStringEvaluating">
https://github.com/KovtunV/NoStringEvaluating
</Hyperlink>
<LineBreak />
- Ookii.Dialogs.Wpf
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
@@ -3,14 +3,10 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.Conditions;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using FluentValidation;
@@ -23,7 +19,6 @@ namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit
{
public class ProfileEditViewModel : DialogViewModelBase
{
private readonly DataModelConditionGroup _dataModelConditionGroup;
private readonly List<Module> _modules;
private readonly IProfileService _profileService;
private bool _changedImage;
@@ -38,14 +33,12 @@ namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit
IProfileService profileService,
IPluginManagementService pluginManagementService,
ISidebarVmFactory sidebarVmFactory,
IDataModelConditionsVmFactory dataModelConditionsVmFactory,
IModelValidator<ProfileEditViewModel> validator) : base(validator)
{
ProfileConfiguration = profileConfiguration;
IsNew = isNew;
_profileService = profileService;
_dataModelConditionGroup = ProfileConfiguration.ActivationCondition ?? new DataModelConditionGroup(null);
_modules = ProfileConfiguration.Module != null ? new List<Module> {ProfileConfiguration.Module} : new List<Module>();
IconTypes = new BindableCollection<ValueDescription>(EnumUtilities.GetAllValuesAndDescriptions(typeof(ProfileConfigurationIconType)));
@@ -55,10 +48,7 @@ namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit
pluginManagementService.GetFeaturesOfType<Module>().Where(m => !m.IsAlwaysAvailable).Select(m => new ProfileModuleViewModel(m))
);
Initializing = true;
ActivationConditionViewModel = dataModelConditionsVmFactory.DataModelConditionGroupViewModel(_dataModelConditionGroup, ConditionGroupType.General, _modules);
ActivationConditionViewModel.ConductWith(this);
ActivationConditionViewModel.IsRootGroup = true;
ModuleActivationRequirementsViewModel = new ModuleActivationRequirementsViewModel(sidebarVmFactory);
ModuleActivationRequirementsViewModel.ConductWith(this);
ModuleActivationRequirementsViewModel.SetModule(ProfileConfiguration.Module);
@@ -85,8 +75,7 @@ namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit
Initializing = false;
});
}
public DataModelConditionGroupViewModel ActivationConditionViewModel { get; }
public ModuleActivationRequirementsViewModel ModuleActivationRequirementsViewModel { get; }
public ProfileConfigurationHotkeyViewModel EnableHotkeyViewModel { get; }
public ProfileConfigurationHotkeyViewModel DisableHotkeyViewModel { get; }
@@ -161,7 +150,6 @@ namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit
if (value != null)
_modules.Add(value.Module);
ActivationConditionViewModel.UpdateModules();
ModuleActivationRequirementsViewModel.SetModule(value?.Module);
}
}
@@ -185,9 +173,6 @@ namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit
ProfileConfiguration.Module = SelectedModule?.Module;
if (_dataModelConditionGroup.Children.Any())
ProfileConfiguration.ActivationCondition = _dataModelConditionGroup;
if (_changedImage)
{
ProfileConfiguration.Icon.FileIcon = SelectedImage;
@@ -19,6 +19,7 @@
<!-- Above is a dumb hack to get the context menu to cover the entire item -->
<UserControl.Resources>
<converters:ValuesAdditionConverter x:Key="ValuesAddition" />
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
</UserControl.Resources>
<UserControl.ContextMenu>
<ContextMenu>
@@ -183,7 +184,13 @@
</Border.Style>
</Border>
<Button Grid.Column="2" ToolTip="View properties" Width="20" Height="20" Command="{s:Action ViewProperties}" HorizontalAlignment="Right">
<Button Grid.Column="2"
ToolTip="View properties"
Width="20"
Height="20"
Command="{s:Action ViewProperties}"
HorizontalAlignment="Right"
Visibility="{Binding ProfileConfiguration.BrokenState, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}">
<Button.Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource MaterialDesignIconForegroundButton}">
<Setter Property="Visibility" Value="Collapsed" />
@@ -196,7 +203,13 @@
</Button.Style>
<materialDesign:PackIcon Kind="Cog" Width="16" Height="16" />
</Button>
<ToggleButton Grid.Column="3" ToolTip="Suspend profile" Width="18" Height="18" Margin="2 0 0 0" IsChecked="{Binding IsSuspended}">
<ToggleButton Grid.Column="3"
ToolTip="Suspend profile"
Width="18"
Height="18"
Margin="2 0 0 0"
IsChecked="{Binding IsSuspended}"
Visibility="{Binding ProfileConfiguration.BrokenState, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}">
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource MaterialDesignFlatToggleButton}">
<Setter Property="Visibility" Value="Collapsed" />
@@ -209,5 +222,14 @@
</ToggleButton.Style>
<materialDesign:PackIcon Kind="Pause" Height="14" Width="14" />
</ToggleButton>
<materialDesign:PackIcon Grid.Column="3"
Margin="-2"
Kind="AlertCircle"
Height="22"
Width="22"
Foreground="#E74C4C"
ToolTip="{Binding ProfileConfiguration.BrokenState}"
Visibility="{Binding ProfileConfiguration.BrokenState, Converter={StaticResource NullToVisibilityConverter}}" />
</Grid>
</UserControl>
@@ -157,6 +157,12 @@ namespace Artemis.UI.Screens.Sidebar
public void SelectProfileConfiguration(ProfileConfiguration profileConfiguration)
{
if (profileConfiguration?.BrokenStateException != null)
{
_dialogService.ShowExceptionDialog("The profile failed to load", profileConfiguration.BrokenStateException);
return;
}
foreach (SidebarCategoryViewModel sidebarCategoryViewModel in Items)
sidebarCategoryViewModel.SelectedProfileConfiguration = sidebarCategoryViewModel.Items.FirstOrDefault(i => i.ProfileConfiguration == profileConfiguration);