Profile editor - Moved files

This commit is contained in:
SpoinkyNL committed 2020-08-15 17:53:12 +02:00
1 parent 0516ccc658
commit 698d1b8bbd
86 files changed
+199 -215

No files matched your search

@@ -0,0 +1,50 @@
using System;
using Artemis.Core.Models.Profile.Conditions.Abstract;
using Artemis.UI.Shared.DataModelVisualization.Shared;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract
{
public abstract class DisplayConditionViewModel : PropertyChangedBase, IDisposable
{
protected DisplayConditionViewModel(DisplayConditionPart model, DisplayConditionViewModel parent)
{
Model = model;
Parent = parent;
Children = new BindableCollection<DisplayConditionViewModel>();
}
public DisplayConditionPart Model { get; }
public DisplayConditionViewModel Parent { get; set; }
public BindableCollection<DisplayConditionViewModel> Children { get; }
public void Dispose()
{
foreach (var child in Children)
child.Dispose();
Dispose(true);
GC.SuppressFinalize(this);
}
public abstract void Update();
public virtual DataModelPropertiesViewModel GetDataModelOverride()
{
return Parent?.GetDataModelOverride();
}
public virtual void Delete()
{
Model.Parent.RemoveChild(Model);
Parent.Update();
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
}
}
}
}
@@ -0,0 +1,135 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.DisplayConditions.DisplayConditionGroupView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.DisplayConditions"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance Type=local:DisplayConditionGroupViewModel, IsDesignTimeCreatable=False}">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DisplayConditions.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="*" />
</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"
ToolTip="Change the operator of the group"
Style="{StaticResource DisplayConditionButtonLeftClickMenu}"
Background="#E74C4C"
BorderBrush="#E74C4C"
Margin="3 1"
Content="{Binding SelectedBooleanOperator}"
Visibility="{Binding DisplayBooleanOperator, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="And" Command="{s:Action SelectBooleanOperator}" CommandParameter="And" />
<MenuItem Header="Or" Command="{s:Action SelectBooleanOperator}" CommandParameter="Or" />
<MenuItem Header="And not" Command="{s:Action SelectBooleanOperator}" CommandParameter="AndNot" />
<MenuItem Header="Or not" Command="{s:Action SelectBooleanOperator}" CommandParameter="OrNot" />
</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="Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="ContextMenu.IsOpen">
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<materialDesign:PackIcon Kind="Add" Width="18" Height="18" />
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Add static condition"
ToolTip="A condition that compares a data model property to a static input"
Command="{s:Action AddCondition}"
CommandParameter="Static">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="FormTextarea" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Add dynamic condition"
ToolTip="A condition that compares two data model properties"
Command="{s:Action AddCondition}"
CommandParameter="Dynamic">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="InsertLink" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Add list condition"
ToolTip="A condition that evaluates on items in a list"
Command="{s:Action AddCondition}"
CommandParameter="List">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="FormatListBulleted" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Add group" ToolTip="A group can contain conditions and other groups" Command="{s:Action AddGroup}">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="CodeParentheses" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</Button.ContextMenu>
</Button>
<ItemsControl Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" ItemsSource="{Binding Children}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<materialDesign:TransitioningContent>
<materialDesign:TransitioningContent.OpeningEffects>
<materialDesign:TransitionEffect Kind="FadeIn" />
<materialDesign:TransitionEffect Kind="SlideInFromLeft" />
</materialDesign:TransitioningContent.OpeningEffects>
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
</materialDesign:TransitioningContent>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
@@ -0,0 +1,124 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using Artemis.Core.Models.Profile.Conditions;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract;
using Artemis.UI.Shared.Services.Interfaces;
using Humanizer;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
{
public class DisplayConditionGroupViewModel : DisplayConditionViewModel, IViewAware
{
private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory;
private readonly IProfileEditorService _profileEditorService;
private bool _isInitialized;
private bool _isRootGroup;
public DisplayConditionGroupViewModel(DisplayConditionGroup displayConditionGroup, DisplayConditionViewModel parent,
IProfileEditorService profileEditorService, IDisplayConditionsVmFactory displayConditionsVmFactory) : base(displayConditionGroup, parent)
{
_profileEditorService = profileEditorService;
_displayConditionsVmFactory = displayConditionsVmFactory;
Children.CollectionChanged += (sender, args) => NotifyOfPropertyChange(nameof(DisplayBooleanOperator));
Execute.PostToUIThread(async () =>
{
await Task.Delay(50);
IsInitialized = true;
});
}
public DisplayConditionGroup DisplayConditionGroup => (DisplayConditionGroup) Model;
public bool IsRootGroup
{
get => _isRootGroup;
set => SetAndNotify(ref _isRootGroup, value);
}
public bool IsInitialized
{
get => _isInitialized;
set => SetAndNotify(ref _isInitialized, value);
}
public bool DisplayBooleanOperator => Children.Count > 1;
public string SelectedBooleanOperator => DisplayConditionGroup.BooleanOperator.Humanize();
public void AttachView(UIElement view)
{
View = view;
}
public UIElement View { get; set; }
public void SelectBooleanOperator(string type)
{
var enumValue = Enum.Parse<BooleanOperator>(type);
DisplayConditionGroup.BooleanOperator = enumValue;
NotifyOfPropertyChange(nameof(SelectedBooleanOperator));
}
public void AddCondition(string type)
{
if (type == "Static")
DisplayConditionGroup.AddChild(new DisplayConditionPredicate(DisplayConditionGroup, PredicateType.Static));
else if (type == "Dynamic")
DisplayConditionGroup.AddChild(new DisplayConditionPredicate(DisplayConditionGroup, PredicateType.Dynamic));
else if (type == "List")
DisplayConditionGroup.AddChild(new DisplayConditionList(DisplayConditionGroup));
Update();
_profileEditorService.UpdateSelectedProfileElement();
}
public void AddGroup()
{
DisplayConditionGroup.AddChild(new DisplayConditionGroup(DisplayConditionGroup));
Update();
_profileEditorService.UpdateSelectedProfileElement();
}
public override void Update()
{
NotifyOfPropertyChange(nameof(SelectedBooleanOperator));
// Remove VMs of effects no longer applied on the layer
var toRemove = Children.Where(c => !DisplayConditionGroup.Children.Contains(c.Model)).ToList();
// Using RemoveRange breaks our lovely animations
foreach (var displayConditionViewModel in toRemove)
{
Children.Remove(displayConditionViewModel);
displayConditionViewModel.Dispose();
}
foreach (var childModel in Model.Children)
{
if (Children.Any(c => c.Model == childModel))
continue;
switch (childModel)
{
case DisplayConditionGroup displayConditionGroup:
Children.Add(_displayConditionsVmFactory.DisplayConditionGroupViewModel(displayConditionGroup, this));
break;
case DisplayConditionList displayConditionListPredicate:
Children.Add(_displayConditionsVmFactory.DisplayConditionListViewModel(displayConditionListPredicate, this));
break;
case DisplayConditionPredicate displayConditionPredicate:
Children.Add(_displayConditionsVmFactory.DisplayConditionPredicateViewModel(displayConditionPredicate, this));
break;
}
}
foreach (var childViewModel in Children)
childViewModel.Update();
}
}
}
@@ -0,0 +1,120 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.DisplayConditions.DisplayConditionListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.DisplayConditions"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:utilities="clr-namespace:Artemis.UI.Utilities"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance Type=local:DisplayConditionListViewModel, IsDesignTimeCreatable=False}">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DisplayConditions.xaml" />
<ResourceDictionary>
<converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<utilities:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
<DataTemplate x:Key="DataModelDataTemplate">
<Control x:Name="TemplateControl" Focusable="False" Template="{StaticResource DataModelSelectionTemplate}" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Data.ShowDataModelValues.Value, Source={StaticResource DataContextProxy}}" Value="True">
<Setter TargetName="TemplateControl" Property="Template" Value="{StaticResource DataModelSelectionTemplateWithValues}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Margin="0 3" Visibility="{Binding IsInitialized, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Row="0"
Grid.Column="0"
ToolTip="Delete the list predicate and all its children"
Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Left"
Foreground="#E74C4C"
Width="25"
Height="25"
Command="{s:Action Delete}">
<materialDesign:PackIcon Kind="Close" Width="18" Height="18" />
</Button>
<!-- Left side, the list this predicate is targeting -->
<Button Grid.Row="0"
Grid.Column="1"
Background="#ab47bc"
BorderBrush="#ab47bc"
Style="{StaticResource DisplayConditionButton}"
ToolTip="{Binding SelectedListProperty.DisplayPropertyPath}"
Click="PropertyButton_OnClick">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding TargetDataModel.Children}" IsOpen="{Binding TargetDataModelOpen, Mode=OneWayToSource}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MaterialDesignMenuItem}">
<Setter Property="ItemsSource" Value="{Binding Children}" />
<Setter Property="Command" Value="{Binding Data.SelectListPropertyCommand, Source={StaticResource DataContextProxy}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
<Setter Property="CommandTarget" Value="{Binding}" />
<Setter Property="IsEnabled" Value="{Binding IsMatchingFilteredTypes}" />
<Setter Property="IsSubmenuOpen" Value="{Binding IsVisualizationExpanded, Mode=TwoWay}" />
<Setter Property="HeaderTemplate" Value="{StaticResource DataModelDataTemplate}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
<Grid>
<TextBlock Text="{Binding SelectedListProperty.PropertyDescription.Name}"
Visibility="{Binding SelectedListProperty, Converter={StaticResource NullToVisibilityConverter}}" />
<TextBlock Text="« Select a list »"
FontStyle="Italic"
Visibility="{Binding SelectedListProperty, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}" />
</Grid>
</Button>
<!-- Operator -->
<Button Grid.Row="0"
Grid.Column="2"
Style="{StaticResource DisplayConditionButtonLeftClickMenu}"
Background="#7B7B7B"
BorderBrush="#7B7B7B"
Content="{Binding SelectedListOperator}"
Click="PropertyButton_OnClick"
HorizontalAlignment="Left">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Any" Command="{s:Action SelectListOperator}" CommandParameter="Any" />
<MenuItem Header="All" Command="{s:Action SelectListOperator}" CommandParameter="All" />
<MenuItem Header="None" Command="{s:Action SelectListOperator}" CommandParameter="None" />
<MenuItem Header="Count (NYI)" Command="{s:Action SelectListOperator}" CommandParameter="Count" IsEnabled="False"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
<ItemsControl Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" ItemsSource="{Binding Children}" Margin="0 4 0 0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<materialDesign:TransitioningContent>
<materialDesign:TransitioningContent.OpeningEffects>
<materialDesign:TransitionEffect Kind="FadeIn" />
<materialDesign:TransitionEffect Kind="SlideInFromLeft" />
</materialDesign:TransitioningContent.OpeningEffects>
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
</materialDesign:TransitioningContent>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
@@ -0,0 +1,26 @@
using System.Windows;
using System.Windows.Controls;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
{
/// <summary>
/// Interaction logic for DisplayConditionListView.xaml
/// </summary>
public partial class DisplayConditionListView : UserControl
{
public DisplayConditionListView()
{
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;
}
}
}
}
@@ -0,0 +1,217 @@
using System;
using System.Collections;
using System.Linq;
using System.Threading.Tasks;
using System.Timers;
using Artemis.Core.Models.Profile.Conditions;
using Artemis.Core.Plugins.Models;
using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract;
using Artemis.UI.Shared.DataModelVisualization.Shared;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Interfaces;
using Artemis.UI.Utilities;
using Humanizer;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
{
public class DisplayConditionListViewModel : DisplayConditionViewModel
{
private readonly IProfileEditorService _profileEditorService;
private readonly IDataModelVisualizationService _dataModelVisualizationService;
private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory;
private bool _isInitialized;
private DataModelListViewModel _selectedListProperty;
private DataModelPropertiesViewModel _targetDataModel;
private readonly Timer _updateTimer;
public DisplayConditionListViewModel(
DisplayConditionList displayConditionList,
DisplayConditionViewModel parent,
IProfileEditorService profileEditorService,
IDataModelVisualizationService dataModelVisualizationService,
IDisplayConditionsVmFactory displayConditionsVmFactory,
ISettingsService settingsService) : base(displayConditionList, parent)
{
_profileEditorService = profileEditorService;
_dataModelVisualizationService = dataModelVisualizationService;
_displayConditionsVmFactory = displayConditionsVmFactory;
_updateTimer = new Timer(500);
SelectListPropertyCommand = new DelegateCommand(ExecuteSelectListProperty);
ShowDataModelValues = settingsService.GetSetting<bool>("ProfileEditor.ShowDataModelValues");
// Initialize async, no need to wait for it
Task.Run(Initialize);
}
public DelegateCommand SelectListPropertyCommand { get; }
public PluginSetting<bool> ShowDataModelValues { get; }
public DisplayConditionList DisplayConditionList => (DisplayConditionList) Model;
public bool IsInitialized
{
get => _isInitialized;
set => SetAndNotify(ref _isInitialized, value);
}
public bool TargetDataModelOpen { get; set; }
public DataModelPropertiesViewModel TargetDataModel
{
get => _targetDataModel;
set => SetAndNotify(ref _targetDataModel, value);
}
public DataModelListViewModel SelectedListProperty
{
get => _selectedListProperty;
set => SetAndNotify(ref _selectedListProperty, value);
}
public string SelectedListOperator => DisplayConditionList.ListOperator.Humanize();
public void SelectListOperator(string type)
{
var enumValue = Enum.Parse<ListOperator>(type);
DisplayConditionList.ListOperator = enumValue;
NotifyOfPropertyChange(nameof(SelectedListOperator));
}
public void AddCondition(string type)
{
if (type == "Static")
DisplayConditionList.AddChild(new DisplayConditionPredicate(DisplayConditionList, PredicateType.Static));
else if (type == "Dynamic")
DisplayConditionList.AddChild(new DisplayConditionPredicate(DisplayConditionList, PredicateType.Dynamic));
Update();
_profileEditorService.UpdateSelectedProfileElement();
}
public void AddGroup()
{
DisplayConditionList.AddChild(new DisplayConditionGroup(DisplayConditionList));
Update();
_profileEditorService.UpdateSelectedProfileElement();
}
public override void Delete()
{
base.Delete();
_profileEditorService.UpdateSelectedProfileElement();
}
public void Initialize()
{
// Get the data models
TargetDataModel = _dataModelVisualizationService.GetMainDataModelVisualization();
if (!_dataModelVisualizationService.GetPluginExtendsDataModel(_profileEditorService.GetCurrentModule()))
TargetDataModel.Children.Add(_dataModelVisualizationService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule()));
TargetDataModel.UpdateRequested += TargetDataModelUpdateRequested;
Update();
_updateTimer.Start();
_updateTimer.Elapsed += OnUpdateTimerOnElapsed;
IsInitialized = true;
}
protected override void Dispose(bool disposing)
{
_updateTimer.Stop();
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
}
private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
{
if (TargetDataModelOpen)
{
TargetDataModel?.Update(_dataModelVisualizationService);
SelectedListProperty?.Update(_dataModelVisualizationService);
}
}
private void TargetDataModelUpdateRequested(object sender, EventArgs e)
{
TargetDataModel.ApplyTypeFilter(true, typeof(IList));
}
public void ApplyList()
{
DisplayConditionList.UpdateList(SelectedListProperty.DataModel, SelectedListProperty.PropertyPath);
_profileEditorService.UpdateSelectedProfileElement();
Update();
}
public override DataModelPropertiesViewModel GetDataModelOverride()
{
if (SelectedListProperty != null)
return SelectedListProperty.GetListTypeViewModel(_dataModelVisualizationService);
return base.GetDataModelOverride();
}
public override void Update()
{
if (TargetDataModel == null)
return;
NotifyOfPropertyChange(nameof(SelectedListOperator));
// Update the selected list property
if (DisplayConditionList.ListDataModel != null && DisplayConditionList.ListPropertyPath != null)
{
var child = TargetDataModel.GetChildByPath(
DisplayConditionList.ListDataModel.PluginInfo.Guid,
DisplayConditionList.ListPropertyPath
);
SelectedListProperty = child as DataModelListViewModel;
}
// Ensure filtering is applied to include Enumerables only
TargetDataModel.ApplyTypeFilter(true, typeof(IList));
// Remove VMs of effects no longer applied on the layer
var toRemove = Children.Where(c => !DisplayConditionList.Children.Contains(c.Model)).ToList();
// Using RemoveRange breaks our lovely animations
foreach (var displayConditionViewModel in toRemove)
{
Children.Remove(displayConditionViewModel);
displayConditionViewModel.Dispose();
}
foreach (var childModel in Model.Children)
{
if (Children.Any(c => c.Model == childModel))
continue;
if (!(childModel is DisplayConditionGroup displayConditionGroup))
continue;
var viewModel = _displayConditionsVmFactory.DisplayConditionGroupViewModel(displayConditionGroup, this);
viewModel.IsRootGroup = true;
Children.Add(viewModel);
}
foreach (var childViewModel in Children)
childViewModel.Update();
}
private void ExecuteSelectListProperty(object context)
{
if (!(context is DataModelListViewModel dataModelListViewModel))
return;
SelectedListProperty = dataModelListViewModel;
ApplyList();
}
}
}
@@ -0,0 +1,187 @@
<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"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:utilities="clr-namespace:Artemis.UI.Utilities"
xmlns:displayConditions="clr-namespace:Artemis.UI.Screens.ProfileEditor.DisplayConditions"
x:Class="Artemis.UI.Screens.ProfileEditor.DisplayConditions.DisplayConditionPredicateView"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance IsDesignTimeCreatable=False, Type={x:Type displayConditions:DisplayConditionPredicateViewModel}}">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DisplayConditions.xaml" />
<ResourceDictionary>
<converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<utilities:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
<DataTemplate x:Key="DataModelDataTemplate">
<Control x:Name="TemplateControl" Focusable="False" Template="{StaticResource DataModelSelectionTemplate}" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Data.ShowDataModelValues.Value, Source={StaticResource DataContextProxy}}" Value="True">
<Setter TargetName="TemplateControl" Property="Template" Value="{StaticResource DataModelSelectionTemplateWithValues}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Margin="0 3" Visibility="{Binding IsInitialized, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Grid.Row="0"
Grid.Column="0"
ToolTip="Delete the predicate"
Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Left"
Foreground="#E74C4C"
Width="25"
Height="25"
Command="{s:Action Delete}">
<materialDesign:PackIcon Kind="Close" Width="18" Height="18" />
</Button>
<!-- Left side, always a property -->
<Button Grid.Row="0"
Grid.Column="1"
Background="#ab47bc"
BorderBrush="#ab47bc"
Style="{StaticResource DisplayConditionButton}"
ToolTip="{Binding SelectedLeftSideProperty.DisplayPropertyPath}"
Click="PropertyButton_OnClick">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding LeftSideDataModel.Children}" IsOpen="{Binding LeftSideDataModelOpen, Mode=OneWayToSource}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MaterialDesignMenuItem}">
<Setter Property="ItemsSource" Value="{Binding Children}" />
<Setter Property="Command" Value="{Binding Data.SelectLeftPropertyCommand, Source={StaticResource DataContextProxy}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
<Setter Property="CommandTarget" Value="{Binding}" />
<Setter Property="IsEnabled" Value="{Binding IsMatchingFilteredTypes}" />
<Setter Property="IsSubmenuOpen" Value="{Binding IsVisualizationExpanded, Mode=TwoWay}" />
<Setter Property="HeaderTemplate" Value="{StaticResource DataModelDataTemplate}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
<Grid>
<TextBlock Text="{Binding SelectedLeftSideProperty.PropertyDescription.Name}"
Visibility="{Binding SelectedLeftSideProperty, Converter={StaticResource NullToVisibilityConverter}}" />
<TextBlock Text="« Select a property »"
FontStyle="Italic"
Visibility="{Binding SelectedLeftSideProperty, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}" />
</Grid>
</Button>
<!-- Operator -->
<Button Grid.Row="0"
Grid.Column="2"
Style="{StaticResource DisplayConditionButtonLeftClickMenu}"
Background="#7B7B7B"
BorderBrush="#7B7B7B"
Content="{Binding SelectedOperator.Description}"
Click="PropertyButton_OnClick">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding Operators}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="{Binding Icon}" VerticalAlignment="Center" Margin="0 0 15 0" />
<TextBlock Text="{Binding Description}" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
</ContextMenu.ItemTemplate>
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MaterialDesignMenuItem}">
<Setter Property="Command" Value="{Binding Data.SelectOperatorCommand, Source={StaticResource DataContextProxy}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
<Setter Property="CommandTarget" Value="{Binding}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
</Button>
<Grid Grid.Row="0"
Grid.Column="3"
Visibility="{Binding SelectedOperator.SupportsRightSide, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
<!-- Right side property if type is dynamic -->
<Button Background="{DynamicResource PrimaryHueMidBrush}"
BorderBrush="{DynamicResource PrimaryHueMidBrush}"
Style="{StaticResource DisplayConditionButton}"
ToolTip="{Binding SelectedRightSideProperty.DisplayPropertyPath}"
Click="PropertyButton_OnClick"
Visibility="{Binding ShowRightSidePropertySelection, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding RightSideDataModel.Children}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MaterialDesignMenuItem}">
<Setter Property="ItemsSource" Value="{Binding Children}" />
<Setter Property="Command" Value="{Binding Data.SelectRightPropertyCommand, Source={StaticResource DataContextProxy}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
<Setter Property="CommandTarget" Value="{Binding}" />
<Setter Property="IsEnabled" Value="{Binding IsMatchingFilteredTypes}" />
<Setter Property="IsSubmenuOpen" Value="{Binding IsVisualizationExpanded, Mode=TwoWay}" />
<Setter Property="HeaderTemplate" Value="{StaticResource DataModelDataTemplate}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
<Grid>
<TextBlock Text="{Binding SelectedRightSideProperty.PropertyDescription.Name}"
Visibility="{Binding SelectedRightSideProperty, Converter={StaticResource NullToVisibilityConverter}}" />
<TextBlock Text="« Select a property »"
FontStyle="Italic"
Visibility="{Binding SelectedRightSideProperty, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}" />
</Grid>
</Button>
<!-- Right side property if type is static -->
<materialDesign:Transitioner SelectedIndex="{Binding RightSideTransitionIndex}"
DefaultTransitionOrigin="0.5, 0.5"
Margin="3 -4"
Visibility="{Binding ShowRightSidePropertySelection, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}">
<Button Style="{StaticResource DisplayConditionButton}"
Background="{DynamicResource PrimaryHueMidBrush}"
BorderBrush="{DynamicResource PrimaryHueMidBrush}"
Margin="0 4"
Command="{s:Action ActivateRightSideInputViewModel}"
HorizontalAlignment="Left">
<Grid>
<StackPanel Visibility="{Binding RightStaticValue, Converter={StaticResource NullToVisibilityConverter}}" Orientation="Horizontal">
<TextBlock FontWeight="Light"
Text="{Binding SelectedLeftSideProperty.PropertyDescription.Prefix}"
Visibility="{Binding SelectedLeftSideProperty.PropertyDescription.Prefix, Converter={StaticResource NullToVisibilityConverter}}" />
<TextBlock Text="{Binding RightStaticValue}" />
<TextBlock FontWeight="Light"
Text="{Binding SelectedLeftSideProperty.PropertyDescription.Affix}"
Visibility="{Binding SelectedLeftSideProperty.PropertyDescription.Affix, Converter={StaticResource NullToVisibilityConverter}}" />
</StackPanel>
<TextBlock Text="« Enter a value »"
FontStyle="Italic"
Visibility="{Binding RightStaticValue, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}" />
</Grid>
</Button>
<Border BorderBrush="{DynamicResource PrimaryHueMidBrush}"
Background="{DynamicResource MaterialDesignPaper}"
CornerRadius="3"
Padding="3"
HorizontalAlignment="Left"
MinWidth="140">
<ContentControl s:View.Model="{Binding RightSideInputViewModel}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
</Border>
</materialDesign:Transitioner>
</Grid>
</Grid>
</UserControl>
@@ -0,0 +1,26 @@
using System.Windows;
using System.Windows.Controls;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
{
/// <summary>
/// Interaction logic for DisplayConditionPredicateView.xaml
/// </summary>
public partial class DisplayConditionPredicateView : UserControl
{
public DisplayConditionPredicateView()
{
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;
}
}
}
}
@@ -0,0 +1,349 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Timers;
using System.Windows;
using System.Windows.Input;
using Artemis.Core.Models.Profile.Conditions;
using Artemis.Core.Plugins.Models;
using Artemis.Core.Services;
using Artemis.Core.Services.Interfaces;
using Artemis.UI.Events;
using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract;
using Artemis.UI.Shared.DataModelVisualization;
using Artemis.UI.Shared.DataModelVisualization.Shared;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Interfaces;
using Artemis.UI.Utilities;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
{
public class DisplayConditionPredicateViewModel : DisplayConditionViewModel, IHandle<MainWindowKeyEvent>, IHandle<MainWindowMouseEvent>
{
private readonly IDataModelService _dataModelService;
private readonly IDataModelVisualizationService _dataModelVisualizationService;
private readonly IEventAggregator _eventAggregator;
private readonly IProfileEditorService _profileEditorService;
private bool _isInitialized;
private DataModelPropertiesViewModel _leftSideDataModel;
private BindableCollection<DisplayConditionOperator> _operators;
private DataModelPropertiesViewModel _rightSideDataModel;
private DataModelInputViewModel _rightSideInputViewModel;
private int _rightSideTransitionIndex;
private object _rightStaticValue;
private DataModelVisualizationViewModel _selectedLeftSideProperty;
private DisplayConditionOperator _selectedOperator;
private DataModelVisualizationViewModel _selectedRightSideProperty;
private List<Type> _supportedInputTypes;
private readonly Timer _updateTimer;
public DisplayConditionPredicateViewModel(
DisplayConditionPredicate displayConditionPredicate,
DisplayConditionViewModel parent,
IProfileEditorService profileEditorService,
IDataModelVisualizationService dataModelVisualizationService,
IDataModelService dataModelService,
ISettingsService settingsService,
IEventAggregator eventAggregator) : base(displayConditionPredicate, parent)
{
_profileEditorService = profileEditorService;
_dataModelVisualizationService = dataModelVisualizationService;
_dataModelService = dataModelService;
_eventAggregator = eventAggregator;
_updateTimer = new Timer(500);
_supportedInputTypes = new List<Type>();
SelectLeftPropertyCommand = new DelegateCommand(ExecuteSelectLeftProperty);
SelectRightPropertyCommand = new DelegateCommand(ExecuteSelectRightProperty);
SelectOperatorCommand = new DelegateCommand(ExecuteSelectOperatorCommand);
Operators = new BindableCollection<DisplayConditionOperator>();
ShowDataModelValues = settingsService.GetSetting<bool>("ProfileEditor.ShowDataModelValues");
// Initialize async, no need to wait for it
Task.Run(Initialize);
}
public DisplayConditionPredicate DisplayConditionPredicate => (DisplayConditionPredicate) Model;
public bool ShowRightSidePropertySelection => DisplayConditionPredicate.PredicateType == PredicateType.Dynamic;
public bool CanActivateRightSideInputViewModel => SelectedLeftSideProperty?.PropertyInfo != null;
public PluginSetting<bool> ShowDataModelValues { get; }
public bool IsInitialized
{
get => _isInitialized;
private set => SetAndNotify(ref _isInitialized, value);
}
public bool LeftSideDataModelOpen { get; set; }
public DataModelPropertiesViewModel LeftSideDataModel
{
get => _leftSideDataModel;
set => SetAndNotify(ref _leftSideDataModel, value);
}
public DataModelPropertiesViewModel RightSideDataModel
{
get => _rightSideDataModel;
set => SetAndNotify(ref _rightSideDataModel, value);
}
public bool RightSideDataModelOpen { get; set; }
public DataModelVisualizationViewModel SelectedLeftSideProperty
{
get => _selectedLeftSideProperty;
set
{
if (!SetAndNotify(ref _selectedLeftSideProperty, value)) return;
NotifyOfPropertyChange(nameof(CanActivateRightSideInputViewModel));
}
}
public DataModelVisualizationViewModel SelectedRightSideProperty
{
get => _selectedRightSideProperty;
set => SetAndNotify(ref _selectedRightSideProperty, value);
}
public object RightStaticValue
{
get => _rightStaticValue;
set => SetAndNotify(ref _rightStaticValue, value);
}
public int RightSideTransitionIndex
{
get => _rightSideTransitionIndex;
set => SetAndNotify(ref _rightSideTransitionIndex, value);
}
public DataModelInputViewModel RightSideInputViewModel
{
get => _rightSideInputViewModel;
set => SetAndNotify(ref _rightSideInputViewModel, value);
}
public BindableCollection<DisplayConditionOperator> Operators
{
get => _operators;
set => SetAndNotify(ref _operators, value);
}
public DisplayConditionOperator SelectedOperator
{
get => _selectedOperator;
set => SetAndNotify(ref _selectedOperator, value);
}
public DelegateCommand SelectLeftPropertyCommand { get; }
public DelegateCommand SelectRightPropertyCommand { get; }
public DelegateCommand SelectOperatorCommand { get; }
public void Handle(MainWindowKeyEvent message)
{
if (RightSideInputViewModel == null)
return;
if (!message.KeyDown && message.EventArgs.Key == Key.Escape)
RightSideInputViewModel.Cancel();
if (!message.KeyDown && message.EventArgs.Key == Key.Enter)
RightSideInputViewModel.Submit();
}
public void Handle(MainWindowMouseEvent message)
{
if (RightSideInputViewModel == null)
return;
if (message.Sender is FrameworkElement frameworkElement && !frameworkElement.IsDescendantOf(RightSideInputViewModel.View))
RightSideInputViewModel.Submit();
}
public override void Delete()
{
base.Delete();
_profileEditorService.UpdateSelectedProfileElement();
}
public void Initialize()
{
// Get the data models
LeftSideDataModel = _dataModelVisualizationService.GetMainDataModelVisualization();
RightSideDataModel = _dataModelVisualizationService.GetMainDataModelVisualization();
if (!_dataModelVisualizationService.GetPluginExtendsDataModel(_profileEditorService.GetCurrentModule()))
{
LeftSideDataModel.Children.Add(_dataModelVisualizationService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule()));
RightSideDataModel.Children.Add(_dataModelVisualizationService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule()));
}
// Determine which types are currently supported
var editors = _dataModelVisualizationService.RegisteredDataModelEditors;
_supportedInputTypes = editors.Select(e => e.SupportedType).ToList();
_supportedInputTypes.AddRange(editors.Where(e => e.CompatibleConversionTypes != null).SelectMany(e => e.CompatibleConversionTypes));
LeftSideDataModel.UpdateRequested += LeftDataModelUpdateRequested;
RightSideDataModel.UpdateRequested += RightDataModelUpdateRequested;
Update();
_updateTimer.Start();
_updateTimer.Elapsed += OnUpdateTimerOnElapsed;
IsInitialized = true;
}
public override void Update()
{
if (LeftSideDataModel == null || DisplayConditionPredicate.PredicateType == PredicateType.Dynamic && RightSideDataModel == null)
return;
if (GetDataModelOverride() != null)
LeftSideDataModel = GetDataModelOverride();
// If static, only allow selecting properties also supported by input
if (DisplayConditionPredicate.PredicateType == PredicateType.Static)
LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray());
// Determine the left side property first
SelectedLeftSideProperty = LeftSideDataModel.GetChildForCondition(DisplayConditionPredicate, DisplayConditionSide.Left);
var leftSideType = SelectedLeftSideProperty?.PropertyInfo?.PropertyType;
// Get the supported operators
Operators.Clear();
Operators.AddRange(_dataModelService.GetCompatibleConditionOperators(leftSideType));
if (DisplayConditionPredicate.Operator == null)
DisplayConditionPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType)));
SelectedOperator = DisplayConditionPredicate.Operator;
// Determine the right side
if (DisplayConditionPredicate.PredicateType == PredicateType.Dynamic)
{
SelectedRightSideProperty = LeftSideDataModel.GetChildForCondition(DisplayConditionPredicate, DisplayConditionSide.Right);
if (GetDataModelOverride() != null)
RightSideDataModel = GetDataModelOverride();
RightSideDataModel.ApplyTypeFilter(true, leftSideType);
}
else
RightStaticValue = DisplayConditionPredicate.RightStaticValue;
}
public void ApplyLeftSide()
{
DisplayConditionPredicate.UpdateLeftSide(SelectedLeftSideProperty.DataModel, SelectedLeftSideProperty.PropertyPath);
_profileEditorService.UpdateSelectedProfileElement();
SelectedOperator = DisplayConditionPredicate.Operator;
Update();
}
public void ApplyRightSideDynamic()
{
DisplayConditionPredicate.UpdateRightSide(SelectedRightSideProperty.DataModel, SelectedRightSideProperty.PropertyPath);
_profileEditorService.UpdateSelectedProfileElement();
Update();
}
public void ApplyRightSideStatic(object value, bool isSubmitted)
{
if (isSubmitted)
{
DisplayConditionPredicate.UpdateRightSide(value);
_profileEditorService.UpdateSelectedProfileElement();
Update();
}
RightSideTransitionIndex = 0;
RightSideInputViewModel = null;
RightStaticValue = value;
_eventAggregator.Unsubscribe(this);
}
public void ApplyOperator()
{
DisplayConditionPredicate.UpdateOperator(SelectedOperator);
_profileEditorService.UpdateSelectedProfileElement();
Update();
}
public void ActivateRightSideInputViewModel()
{
if (SelectedLeftSideProperty?.PropertyInfo == null)
return;
RightSideTransitionIndex = 1;
RightSideInputViewModel = _dataModelVisualizationService.GetDataModelInputViewModel(
SelectedLeftSideProperty.PropertyInfo.PropertyType,
SelectedLeftSideProperty.PropertyDescription,
DisplayConditionPredicate.RightStaticValue,
ApplyRightSideStatic
);
_eventAggregator.Subscribe(this);
}
protected override void Dispose(bool disposing)
{
_updateTimer.Stop();
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
}
private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
{
if (LeftSideDataModelOpen)
LeftSideDataModel.Update(_dataModelVisualizationService);
else if (RightSideDataModelOpen)
RightSideDataModel.Update(_dataModelVisualizationService);
}
private void RightDataModelUpdateRequested(object sender, EventArgs e)
{
var leftSideType = SelectedLeftSideProperty?.PropertyInfo?.PropertyType;
if (DisplayConditionPredicate.PredicateType == PredicateType.Dynamic)
SelectedRightSideProperty = LeftSideDataModel.GetChildForCondition(DisplayConditionPredicate, DisplayConditionSide.Right);
// With the data model updated, also reapply the filter
RightSideDataModel.ApplyTypeFilter(true, leftSideType);
}
private void LeftDataModelUpdateRequested(object sender, EventArgs e)
{
if (DisplayConditionPredicate.PredicateType == PredicateType.Static)
LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray());
}
private void ExecuteSelectLeftProperty(object context)
{
if (!(context is DataModelVisualizationViewModel dataModelVisualizationViewModel))
return;
SelectedLeftSideProperty = dataModelVisualizationViewModel;
ApplyLeftSide();
}
private void ExecuteSelectRightProperty(object context)
{
if (!(context is DataModelVisualizationViewModel dataModelVisualizationViewModel))
return;
SelectedRightSideProperty = dataModelVisualizationViewModel;
ApplyRightSideDynamic();
}
private void ExecuteSelectOperatorCommand(object context)
{
if (!(context is DisplayConditionOperator displayConditionOperator))
return;
SelectedOperator = displayConditionOperator;
ApplyOperator();
}
}
}
@@ -0,0 +1,180 @@
<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"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:displayConditions="clr-namespace:Artemis.UI.Screens.ProfileEditor.DisplayConditions"
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" />
</UserControl.Resources>
<materialDesign:Transitioner AutoApplyTransitionOrigins="True" SelectedIndex="{Binding TransitionerIndex}">
<!-- Conditions intro -->
<materialDesign:ColorZone Mode="PrimaryDark" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" VerticalContentAlignment="Stretch">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Margin="32">
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" TextWrapping="Wrap" TextAlignment="Center" Margin="0 15">Unleash Artemis's true potential</TextBlock>
<TextBlock TextWrapping="Wrap" TextAlignment="Center">Start using conditions to dynamically show and hide groups and layers</TextBlock>
<Button Style="{DynamicResource MaterialDesignFloatingActionAccentButton}" Command="{x:Static materialDesign:Transitioner.MoveNextCommand}" Margin="16">
<materialDesign:PackIcon Kind="ArrowRight" />
</Button>
</StackPanel>
</materialDesign:ColorZone>
<!-- Conditions content -->
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}" Margin="0 0 0 -4">
Display conditions
</TextBlock>
<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 RootGroup}" />
</ScrollViewer>
</Grid>
<Grid Grid.Row="3">
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MinWidth="140" />
<ColumnDefinition Width="*" MinWidth="170" />
</Grid.ColumnDefinitions>
<!-- Play mode -->
<StackPanel Grid.Column="0" Orientation="Horizontal">
<materialDesign:PackIcon Kind="PlayOutline" VerticalAlignment="Center" />
<TextBlock 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>
</StackPanel>
<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>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="Repeat" VerticalAlignment="Center" />
<TextBlock FontSize="12" VerticalAlignment="Center">REPEAT</TextBlock>
</StackPanel>
</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>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="StopwatchOutline" VerticalAlignment="Center" />
<TextBlock FontSize="12" VerticalAlignment="Center">ONCE</TextBlock>
</StackPanel>
</RadioButton>
</Grid>
</materialDesign:ColorZone>
<!-- Stop mode -->
<StackPanel Grid.Column="1" Orientation="Horizontal">
<materialDesign:PackIcon Kind="Stop" VerticalAlignment="Center" />
<TextBlock Text="Stop mode" VerticalAlignment="Center">
<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>
</StackPanel>
<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>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="PlayOutline" VerticalAlignment="Center" />
<TextBlock FontSize="12" VerticalAlignment="Center">FINISH</TextBlock>
</StackPanel>
</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>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="SkipNextOutline" VerticalAlignment="Center" />
<TextBlock FontSize="12" VerticalAlignment="Center">SKIP TO END</TextBlock>
</StackPanel>
</RadioButton>
</Grid>
</materialDesign:ColorZone>
</Grid>
</Grid>
</materialDesign:Transitioner>
</UserControl>
@@ -0,0 +1,110 @@
using System.Linq;
using Artemis.Core.Models.Profile;
using Artemis.Core.Models.Profile.Conditions;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Shared.Events;
using Artemis.UI.Shared.Services.Interfaces;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
{
public class DisplayConditionsViewModel : ProfileEditorPanelViewModel
{
private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory;
private readonly IProfileEditorService _profileEditorService;
private RenderProfileElement _renderProfileElement;
private DisplayConditionGroupViewModel _rootGroup;
private int _transitionerIndex;
private bool _displayContinuously;
private bool _alwaysFinishTimeline;
public DisplayConditionsViewModel(IProfileEditorService profileEditorService, IDisplayConditionsVmFactory displayConditionsVmFactory)
{
_profileEditorService = profileEditorService;
_displayConditionsVmFactory = displayConditionsVmFactory;
}
public int TransitionerIndex
{
get => _transitionerIndex;
set => SetAndNotify(ref _transitionerIndex, value);
}
public DisplayConditionGroupViewModel RootGroup
{
get => _rootGroup;
set => SetAndNotify(ref _rootGroup, value);
}
public RenderProfileElement RenderProfileElement
{
get => _renderProfileElement;
set => SetAndNotify(ref _renderProfileElement, value);
}
public bool DisplayContinuously
{
get => _displayContinuously;
set
{
if (!SetAndNotify(ref _displayContinuously, value)) return;
_profileEditorService.UpdateSelectedProfileElement();
}
}
public bool AlwaysFinishTimeline
{
get => _alwaysFinishTimeline;
set
{
if (!SetAndNotify(ref _alwaysFinishTimeline, value)) return;
_profileEditorService.UpdateSelectedProfileElement();
}
}
public bool ConditionBehaviourEnabled => RenderProfileElement != null;
protected override void OnActivate()
{
_profileEditorService.ProfileElementSelected += ProfileEditorServiceOnProfileElementSelected;
}
protected override void OnDeactivate()
{
_profileEditorService.ProfileElementSelected -= ProfileEditorServiceOnProfileElementSelected;
RootGroup?.Dispose();
RootGroup = null;
}
private void ProfileEditorServiceOnProfileElementSelected(object sender, RenderProfileElementEventArgs e)
{
RenderProfileElement = e.RenderProfileElement;
NotifyOfPropertyChange(nameof(ConditionBehaviourEnabled));
_displayContinuously = RenderProfileElement?.DisplayContinuously ?? false;
NotifyOfPropertyChange(nameof(DisplayContinuously));
_alwaysFinishTimeline = RenderProfileElement?.AlwaysFinishTimeline ?? false;
NotifyOfPropertyChange(nameof(AlwaysFinishTimeline));
if (e.RenderProfileElement == null)
{
RootGroup?.Dispose();
RootGroup = null;
return;
}
// Ensure the layer has a root display condition group
if (e.RenderProfileElement.DisplayConditionGroup == null)
e.RenderProfileElement.DisplayConditionGroup = new DisplayConditionGroup(null);
RootGroup?.Dispose();
RootGroup = _displayConditionsVmFactory.DisplayConditionGroupViewModel(e.RenderProfileElement.DisplayConditionGroup, null);
RootGroup.IsRootGroup = true;
RootGroup.Update();
// Only show the intro to conditions once, and only if the layer has no conditions
if (TransitionerIndex != 1)
TransitionerIndex = RootGroup.Children.Any() ? 1 : 0;
}
}
}