Profile editor - Fixed new profile elements not always appearing

UI - Fixed all the memory leaks I could find, closing the UI now frees a lot of RAM
Noise brush - Decreased memory usage by removing unused 4D noise
Layout editor - Fixed device properties not always applying
This commit is contained in:
SpoinkyNL committed 2020-07-29 00:29:05 +02:00
1 parent a729e8f361
commit df1b4cf6ef
25 files changed
+336 -381

No files matched your search

@@ -9,8 +9,7 @@ namespace Artemis.UI.Converters
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType == typeof(bool))
return !(bool) value;
@@ -20,10 +19,14 @@ namespace Artemis.UI.Converters
throw new InvalidOperationException("The target must be a boolean");
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
if (targetType == typeof(bool))
return !(bool) value;
if (targetType == typeof(bool?))
return !(bool?) value;
throw new InvalidOperationException("The target must be a boolean");
}
#endregion
@@ -4,6 +4,7 @@ using Artemis.Core.Models.Profile.LayerProperties;
using Artemis.UI.Shared.PropertyInput;
using Artemis.UI.Shared.Services.Interfaces;
using Artemis.UI.Shared.Utilities;
using Stylet;
namespace Artemis.UI.PropertyInput
{
@@ -11,9 +12,9 @@ namespace Artemis.UI.PropertyInput
{
public EnumPropertyInputViewModel(LayerProperty<T> layerProperty, IProfileEditorService profileEditorService) : base(layerProperty, profileEditorService)
{
EnumValues = EnumUtilities.GetAllValuesAndDescriptions(typeof(T));
EnumValues = new BindableCollection<ValueDescription>(EnumUtilities.GetAllValuesAndDescriptions(typeof(T)));
}
public IEnumerable<ValueDescription> EnumValues { get; }
public BindableCollection<ValueDescription> EnumValues { get; }
}
}
@@ -13,30 +13,11 @@
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DisplayConditions.xaml" />
<ResourceDictionary>
<Style TargetType="Grid" x:Key="InitializingFade">
<Style.Triggers>
<DataTrigger Binding="{Binding IsInitialized}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0.0" To="1.0" Duration="0:0:0.25" BeginTime="0:0:0.1">
<DoubleAnimation.EasingFunction>
<QuadraticEase EasingMode="EaseInOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Style="{StaticResource InitializingFade}" Opacity="0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
@@ -127,7 +108,13 @@
<ItemsControl Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" ItemsSource="{Binding Children}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
<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>
@@ -70,7 +70,9 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
// Remove VMs of effects no longer applied on the layer
var toRemove = Children.Where(c => !DisplayConditionGroup.Children.Contains(c.Model)).ToList();
Children.RemoveRange(toRemove);
// Using RemoveRange breaks our lovely animations
foreach (var displayConditionViewModel in toRemove)
Children.Remove(displayConditionViewModel);
foreach (var childModel in Model.Children)
{
@@ -20,28 +20,11 @@
<ResourceDictionary>
<converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<utilities:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
<Style TargetType="Grid" x:Key="InitializingFade">
<Style.Triggers>
<DataTrigger Binding="{Binding IsInitialized}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0.0" To="1.0" Duration="0:0:0.25" BeginTime="0:0:0.1">
<DoubleAnimation.EasingFunction>
<QuadraticEase EasingMode="EaseInOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Margin="0 3" Style="{StaticResource InitializingFade}" Opacity="0">
<Grid Margin="0 3" Visibility="{Binding IsInitialized, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
@@ -171,13 +171,13 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
LeftSideDataModel.UpdateRequested += LeftDataModelUpdateRequested;
RightSideDataModel.UpdateRequested += RightDataModelUpdateRequested;
IsInitialized = true;
Update();
IsInitialized = true;
}
public override void Update()
{
if (!IsInitialized)
if (LeftSideDataModel == null || (DisplayConditionPredicate.PredicateType == PredicateType.Dynamic && RightSideDataModel == null))
return;
// If static, only allow selecting properties also supported by input
@@ -6,10 +6,14 @@
xmlns:local="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
x:Class="Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions.DisplayConditionsView"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance {x:Type local:DisplayConditionsViewModel}}">
<UserControl.Resources>
<converters:InverseBooleanConverter x:Key="InverseBooleanConverter" />
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
@@ -29,10 +33,13 @@
</Grid>
<StackPanel Grid.Row="3" Margin="10" HorizontalAlignment="Right" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Margin="0 0 10 0">When conditions no longer met</TextBlock>
<ListBox Style="{StaticResource MaterialDesignToolToggleListBox}" SelectedIndex="{Binding ConditionBehaviourIndex}" IsEnabled="{Binding ConditionBehaviourEnabled}" Height="22">
<ListBoxItem Padding="10 0">
<ListBoxItem.ToolTip>
<TextBlock VerticalAlignment="Center" Margin="0 0 5 0">On end: </TextBlock>
<StackPanel Orientation="Horizontal">
<RadioButton Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding RenderProfileElement.AlwaysFinishTimeline}"
Padding="5 0">
<RadioButton.ToolTip>
<ToolTip Placement="Top" VerticalOffset="-5">
<StackPanel>
<TextBlock>
@@ -40,16 +47,18 @@
</TextBlock>
</StackPanel>
</ToolTip>
</ListBoxItem.ToolTip>
</RadioButton.ToolTip>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="PlayArrow" Width="20" Height="20" Margin="0 -4" />
<TextBlock Margin="5 0 0 0" FontSize="11">
<materialDesign:PackIcon Kind="PlayArrow" Width="20" Height="20" VerticalAlignment="Center" />
<TextBlock Margin="5 0 0 0" FontSize="12" VerticalAlignment="Center">
WAIT FOR FINISH
</TextBlock>
</StackPanel>
</ListBoxItem>
<ListBoxItem Padding="10 0">
<ListBoxItem.ToolTip>
</RadioButton>
<RadioButton Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding RenderProfileElement.AlwaysFinishTimeline, Converter={StaticResource InverseBooleanConverter}}"
Padding="0">
<RadioButton.ToolTip>
<ToolTip Placement="Top" VerticalOffset="-5">
<StackPanel>
<TextBlock>
@@ -57,15 +66,15 @@
</TextBlock>
</StackPanel>
</ToolTip>
</ListBoxItem.ToolTip>
</RadioButton.ToolTip>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="SkipNext" Width="20" Height="20" Margin="0 -4" />
<TextBlock Margin="5 0 0 0" FontSize="11">
<materialDesign:PackIcon Kind="SkipNext" Width="20" Height="20" VerticalAlignment="Center" Margin="-5 0 0 0"/>
<TextBlock Margin="5 0 0 0" FontSize="12" VerticalAlignment="Center">
SKIP
</TextBlock>
</StackPanel>
</ListBoxItem>
</ListBox>
</RadioButton>
</StackPanel>
</StackPanel>
</Grid>
</UserControl>
@@ -1,9 +1,11 @@
using Artemis.Core.Models.Profile;
using System.Threading.Tasks;
using Artemis.Core.Models.Profile;
using Artemis.Core.Models.Profile.Conditions;
using Artemis.Storage.Entities.Profile.Abstract;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Shared.Events;
using Artemis.UI.Shared.Services.Interfaces;
using Stylet;
namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
{
@@ -18,7 +20,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
{
_profileEditorService = profileEditorService;
_displayConditionsVmFactory = displayConditionsVmFactory;
profileEditorService.ProfileElementSelected += ProfileEditorServiceOnProfileElementSelected;
}
public DisplayConditionGroupViewModel RootGroup
@@ -42,8 +44,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
return;
RenderProfileElement.AlwaysFinishTimeline = value == 0;
NotifyOfPropertyChange(nameof(ConditionBehaviourIndex));
_profileEditorService.UpdateSelectedProfileElement();
}
}
@@ -70,5 +70,15 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
RootGroup.IsRootGroup = true;
RootGroup.Update();
}
protected override void OnActivate()
{
_profileEditorService.ProfileElementSelected += ProfileEditorServiceOnProfileElementSelected;
}
protected override void OnDeactivate()
{
_profileEditorService.ProfileElementSelected -= ProfileEditorServiceOnProfileElementSelected;
}
}
}
@@ -8,11 +8,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract
public abstract class LayerPropertyBaseViewModel : PropertyChangedBase, IDisposable
{
private bool _isExpanded;
private List<LayerPropertyBaseViewModel> _children;
private BindableCollection<LayerPropertyBaseViewModel> _children;
protected LayerPropertyBaseViewModel()
{
Children = new List<LayerPropertyBaseViewModel>();
Children = new BindableCollection<LayerPropertyBaseViewModel>();
}
public abstract bool IsVisible { get; }
@@ -23,7 +23,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract
set => SetAndNotify(ref _isExpanded, value);
}
public List<LayerPropertyBaseViewModel> Children
public BindableCollection<LayerPropertyBaseViewModel> Children
{
get => _children;
set => SetAndNotify(ref _children, value);
@@ -164,6 +164,16 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
ProfileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged;
PopulateProperties(null);
TimelineViewModel?.Dispose();
TimelineViewModel = null;
StartTimelineSegmentViewModel?.Dispose();
StartTimelineSegmentViewModel = null;
MainTimelineSegmentViewModel?.Dispose();
MainTimelineSegmentViewModel = null;
EndTimelineSegmentViewModel?.Dispose();
EndTimelineSegmentViewModel = null;
base.OnClose();
}
@@ -256,9 +266,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
ApplyLayerBrush();
ApplyEffects();
}
private void SelectedLayerOnLayerBrushUpdated(object sender, EventArgs e)
{
ApplyLayerBrush();
@@ -31,6 +31,18 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
LayerPropertyKeyframe.EasingFunction,
LayerPropertyKeyframe.LayerProperty
);
// If possible, shift the keyframe to the right by 11 pixels
var desiredPosition = newKeyframe.Position + TimeSpan.FromMilliseconds(1000f / _profileEditorService.PixelsPerSecond * 11);
if (desiredPosition <= newKeyframe.LayerProperty.ProfileElement.TimelineLength)
newKeyframe.Position = desiredPosition;
// Otherwise if possible shift it to the left by 11 pixels
else
{
desiredPosition = newKeyframe.Position - TimeSpan.FromMilliseconds(1000f / _profileEditorService.PixelsPerSecond * 11);
if (desiredPosition > TimeSpan.Zero)
newKeyframe.Position = desiredPosition;
}
LayerPropertyKeyframe.LayerProperty.AddKeyframe(newKeyframe);
_profileEditorService.UpdateSelectedProfileElement();
}
@@ -140,14 +140,14 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
if (Segment == SegmentViewModelType.Start)
{
// Remove keyframes that fall in this segment
foreach (var baseLayerPropertyKeyframe in keyframes.Where(k => k.Position < startSegmentEnd))
foreach (var baseLayerPropertyKeyframe in keyframes.Where(k => k.Position <= startSegmentEnd))
baseLayerPropertyKeyframe.Remove();
SelectedProfileElement.StartSegmentLength = TimeSpan.Zero;
}
else if (Segment == SegmentViewModelType.Main)
{
// Remove keyframes that fall in this segment
foreach (var baseLayerPropertyKeyframe in keyframes.Where(k => k.Position > startSegmentEnd && k.Position < mainSegmentEnd))
foreach (var baseLayerPropertyKeyframe in keyframes.Where(k => k.Position > startSegmentEnd && k.Position <= mainSegmentEnd))
baseLayerPropertyKeyframe.Remove();
SelectedProfileElement.MainSegmentLength = TimeSpan.Zero;
}
@@ -189,7 +189,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
else if (Segment == SegmentViewModelType.End)
segmentEnd = SelectedProfileElement.TimelineLength;
foreach (var baseLayerPropertyKeyframe in SelectedProfileElement.GetAllKeyframes().Where(k => k.Position >= segmentEnd))
foreach (var baseLayerPropertyKeyframe in SelectedProfileElement.GetAllKeyframes().Where(k => k.Position > segmentEnd))
baseLayerPropertyKeyframe.Position += amount;
}
@@ -7,6 +7,7 @@ using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using Artemis.Core.Models.Profile;
using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract;
using Artemis.UI.Shared.Services.Interfaces;
using Artemis.UI.Shared.Utilities;
@@ -27,13 +28,16 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
LayerPropertyGroups = layerPropertyGroups;
SelectionRectangle = new RectangleGeometry();
SelectedProfileElement = layerPropertiesViewModel.SelectedProfileElement;
_profileEditorService.SelectedProfileElement.PropertyChanged += SelectedProfileElementOnPropertyChanged;
SelectedProfileElement.PropertyChanged += SelectedProfileElementOnPropertyChanged;
_profileEditorService.PixelsPerSecondChanged += ProfileEditorServiceOnPixelsPerSecondChanged;
Update();
}
public RenderProfileElement SelectedProfileElement { get; set; }
public BindableCollection<LayerPropertyGroupViewModel> LayerPropertyGroups { get; }
public RectangleGeometry SelectionRectangle
@@ -42,21 +46,21 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
set => SetAndNotify(ref _selectionRectangle, value);
}
public double StartSegmentWidth => _profileEditorService.PixelsPerSecond * _profileEditorService.SelectedProfileElement?.StartSegmentLength.TotalSeconds ?? 0;
public double StartSegmentWidth => _profileEditorService.PixelsPerSecond * SelectedProfileElement.StartSegmentLength.TotalSeconds;
public double StartSegmentEndPosition => StartSegmentWidth;
public double MainSegmentWidth => _profileEditorService.PixelsPerSecond * _profileEditorService.SelectedProfileElement?.MainSegmentLength.TotalSeconds ?? 0;
public double MainSegmentWidth => _profileEditorService.PixelsPerSecond * SelectedProfileElement.MainSegmentLength.TotalSeconds;
public double MainSegmentEndPosition => StartSegmentWidth + MainSegmentWidth;
public double EndSegmentWidth => _profileEditorService.PixelsPerSecond * _profileEditorService.SelectedProfileElement?.EndSegmentLength.TotalSeconds ?? 0;
public double EndSegmentWidth => _profileEditorService.PixelsPerSecond * SelectedProfileElement.EndSegmentLength.TotalSeconds;
public double EndSegmentEndPosition => StartSegmentWidth + MainSegmentWidth + EndSegmentWidth;
public double TotalTimelineWidth => _profileEditorService.PixelsPerSecond * _profileEditorService.SelectedProfileElement?.TimelineLength.TotalSeconds ?? 0;
public double TotalTimelineWidth => _profileEditorService.PixelsPerSecond * SelectedProfileElement.TimelineLength.TotalSeconds;
public bool StartSegmentEnabled => _profileEditorService.SelectedProfileElement?.StartSegmentLength != TimeSpan.Zero;
public bool EndSegmentEnabled => _profileEditorService.SelectedProfileElement?.EndSegmentLength != TimeSpan.Zero;
public bool StartSegmentEnabled => SelectedProfileElement.StartSegmentLength != TimeSpan.Zero;
public bool EndSegmentEnabled => SelectedProfileElement.EndSegmentLength != TimeSpan.Zero;
public void Dispose()
{
_profileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged;
_profileEditorService.SelectedProfileElement.PropertyChanged -= SelectedProfileElementOnPropertyChanged;
SelectedProfileElement.PropertyChanged -= SelectedProfileElementOnPropertyChanged;
}
private void SelectedProfileElementOnPropertyChanged(object sender, PropertyChangedEventArgs e)
@@ -115,8 +119,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
public void KeyframeMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Released)
return;
// if (e.LeftButton == MouseButtonState.Released)
// return;
var viewModel = (sender as Ellipse)?.DataContext as TimelineKeyframeViewModel;
if (viewModel == null)
@@ -220,6 +224,18 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
foreach (var keyframeViewModel in keyframeViewModels.Where(k => k.IsSelected))
keyframeViewModel.SaveOffsetToKeyframe(sourceKeyframeViewModel);
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
{
cursorTime = _profileEditorService.SnapToTimeline(
cursorTime,
TimeSpan.FromMilliseconds(1000f / _profileEditorService.PixelsPerSecond * 5),
true,
false,
true,
sourceKeyframeViewModel.BaseLayerPropertyKeyframe
);
}
sourceKeyframeViewModel.UpdatePosition(cursorTime);
foreach (var keyframeViewModel in keyframeViewModels.Where(k => k.IsSelected))
@@ -5,6 +5,7 @@ using System.Windows;
using Artemis.Core.Models.Profile;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem;
using Artemis.UI.Shared.Events;
using Artemis.UI.Shared.Services.Interfaces;
using GongSolutions.Wpf.DragDrop;
using Stylet;
@@ -109,6 +110,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree
return;
}
RootFolder?.Dispose();
RootFolder = _folderVmFactory.Create(folder);
_updatingTree = false;
@@ -168,9 +170,9 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree
#region Event handlers
private void OnProfileElementSelected(object sender, EventArgs e)
private void OnProfileElementSelected(object sender, RenderProfileElementEventArgs e)
{
if (_profileEditorService.SelectedProfileElement == SelectedTreeItem?.ProfileElement)
if (e.RenderProfileElement == SelectedTreeItem?.ProfileElement)
return;
if (RootFolder == null)
@@ -182,13 +184,13 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree
_updatingTree = true;
RootFolder.UpdateProfileElements();
_updatingTree = false;
if (_profileEditorService.SelectedProfileElement == null)
if (e.RenderProfileElement == null)
SelectedTreeItem = null;
else
{
var vms = RootFolder.GetAllChildren();
vms.Add(RootFolder);
SelectedTreeItem = vms.FirstOrDefault(vm => vm.ProfileElement == _profileEditorService.SelectedProfileElement);
var match = RootFolder.GetAllChildren().FirstOrDefault(vm => vm.ProfileElement == e.RenderProfileElement);
if (match != null)
SelectedTreeItem = match;
}
}
@@ -16,10 +16,9 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem
{
private readonly IDialogService _dialogService;
private readonly IFolderVmFactory _folderVmFactory;
private readonly IRenderElementService _renderElementService;
private readonly ILayerVmFactory _layerVmFactory;
private readonly IProfileEditorService _profileEditorService;
private BindableCollection<TreeItemViewModel> _children;
private readonly IRenderElementService _renderElementService;
private TreeItemViewModel _parent;
private ProfileElement _profileElement;
@@ -41,6 +40,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem
ProfileElement = profileElement;
Children = new BindableCollection<TreeItemViewModel>();
ProfileElement.ChildAdded += ProfileElementOnChildAdded;
ProfileElement.ChildRemoved += ProfileElementOnChildRemoved;
UpdateProfileElements();
}
@@ -56,16 +59,15 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem
set => SetAndNotify(ref _profileElement, value);
}
public BindableCollection<TreeItemViewModel> Children
{
get => _children;
set => SetAndNotify(ref _children, value);
}
public BindableCollection<TreeItemViewModel> Children { get; }
public abstract bool SupportsChildren { get; }
public void Dispose()
{
ProfileElement.ChildAdded -= ProfileElementOnChildAdded;
ProfileElement.ChildRemoved -= ProfileElementOnChildRemoved;
Dispose(true);
GC.SuppressFinalize(this);
}
@@ -94,7 +96,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem
Parent.ProfileElement.RemoveChild(source.ProfileElement);
Parent.ProfileElement.AddChild(source.ProfileElement, ProfileElement.Order);
Parent.UpdateProfileElements();
}
public void SetElementBehind(TreeItemViewModel source)
@@ -107,7 +108,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem
Parent.ProfileElement.RemoveChild(source.ProfileElement);
Parent.ProfileElement.AddChild(source.ProfileElement, ProfileElement.Order + 1);
Parent.UpdateProfileElements();
}
public void RemoveExistingElement(TreeItemViewModel treeItem)
@@ -118,6 +118,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem
ProfileElement.RemoveChild(treeItem.ProfileElement);
Children.Remove(treeItem);
treeItem.Parent = null;
treeItem.Dispose();
}
public void AddExistingElement(TreeItemViewModel treeItem)
@@ -136,7 +137,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem
throw new ArtemisUIException("Cannot add a folder to a profile element of type " + ProfileElement.GetType().Name);
ProfileElement.AddChild(new Folder(ProfileElement.Profile, ProfileElement, "New folder"));
UpdateProfileElements();
_profileEditorService.UpdateSelectedProfile();
}
@@ -146,7 +146,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem
throw new ArtemisUIException("Cannot add a layer to a profile element of type " + ProfileElement.GetType().Name);
_renderElementService.CreateLayer(ProfileElement.Profile, ProfileElement, "New layer");
UpdateProfileElements();
_profileEditorService.UpdateSelectedProfile();
}
@@ -182,7 +181,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem
var parent = Parent;
ProfileElement.Parent?.RemoveChild(ProfileElement);
parent.RemoveExistingElement(this);
parent.UpdateProfileElements();
_profileEditorService.UpdateSelectedProfile();
}
@@ -236,5 +234,15 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem
{
}
}
private void ProfileElementOnChildRemoved(object sender, EventArgs e)
{
UpdateProfileElements();
}
private void ProfileElementOnChildAdded(object sender, EventArgs e)
{
UpdateProfileElements();
}
}
}
+65 -11
View File
@@ -1,4 +1,5 @@
using System.ComponentModel;
using System;
using System.ComponentModel;
using System.Reflection;
using System.Threading.Tasks;
using System.Timers;
@@ -21,45 +22,50 @@ namespace Artemis.UI.Screens
{
public class RootViewModel : Conductor<IScreen>
{
private readonly IRegistrationService _builtInRegistrationService;
private readonly ISnackbarMessageQueue _snackbarMessageQueue;
private readonly PluginSetting<ApplicationColorScheme> _colorScheme;
private readonly ICoreService _coreService;
private readonly IDebugService _debugService;
private readonly IRegistrationService _builtInRegistrationService;
private readonly IEventAggregator _eventAggregator;
private readonly ThemeWatcher _themeWatcher;
private readonly Timer _titleUpdateTimer;
private readonly PluginSetting<WindowSize> _windowSize;
private bool _lostFocus;
private bool _isSidebarVisible;
private bool _activeItemReady;
private bool _isSidebarVisible;
private bool _lostFocus;
private string _windowTitle;
private ISnackbarMessageQueue _mainMessageQueue;
public RootViewModel(IEventAggregator eventAggregator, SidebarViewModel sidebarViewModel, ISettingsService settingsService, ICoreService coreService,
IDebugService debugService, IRegistrationService builtInRegistrationService, ISnackbarMessageQueue snackbarMessageQueue)
{
SidebarViewModel = sidebarViewModel;
MainMessageQueue = snackbarMessageQueue;
_eventAggregator = eventAggregator;
_coreService = coreService;
_debugService = debugService;
_builtInRegistrationService = builtInRegistrationService;
_snackbarMessageQueue = snackbarMessageQueue;
_titleUpdateTimer = new Timer(500);
_titleUpdateTimer.Elapsed += (sender, args) => UpdateWindowTitle();
_colorScheme = settingsService.GetSetting("UI.ColorScheme", ApplicationColorScheme.Automatic);
_windowSize = settingsService.GetSetting<WindowSize>("UI.RootWindowSize");
_colorScheme.SettingChanged += (sender, args) => ApplyColorSchemeSetting();
_themeWatcher = new ThemeWatcher();
_themeWatcher.ThemeChanged += (sender, args) => ApplyWindowsTheme(args.Theme);
ApplyColorSchemeSetting();
ActiveItem = SidebarViewModel.SelectedItem;
ActiveItemReady = true;
SidebarViewModel.PropertyChanged += SidebarViewModelOnPropertyChanged;
}
public SidebarViewModel SidebarViewModel { get; }
public ISnackbarMessageQueue MainMessageQueue { get; }
public ISnackbarMessageQueue MainMessageQueue
{
get => _mainMessageQueue;
set => SetAndNotify(ref _mainMessageQueue, value);
}
public bool IsSidebarVisible
{
@@ -112,6 +118,7 @@ namespace Artemis.UI.Screens
{
_eventAggregator.Publish(new MainWindowKeyEvent(sender, false, e));
}
public void WindowMouseDown(object sender, MouseButtonEventArgs e)
{
_eventAggregator.Publish(new MainWindowMouseEvent(sender, true, e));
@@ -176,6 +183,21 @@ namespace Artemis.UI.Screens
extensionsPaletteHelper.SetLightDark(colorScheme == ApplicationColorScheme.Dark);
}
private void OnTitleUpdateTimerOnElapsed(object sender, ElapsedEventArgs args)
{
UpdateWindowTitle();
}
private void ThemeWatcherOnThemeChanged(object sender, WindowsThemeEventArgs e)
{
ApplyWindowsTheme(e.Theme);
}
private void ColorSchemeOnSettingChanged(object sender, EventArgs e)
{
ApplyColorSchemeSetting();
}
#region Overrides of Screen
protected override void OnViewLoaded()
@@ -194,22 +216,54 @@ namespace Artemis.UI.Screens
protected override void OnActivate()
{
MainMessageQueue = _snackbarMessageQueue;
UpdateWindowTitle();
_titleUpdateTimer.Start();
_builtInRegistrationService.RegisterBuiltInDataModelDisplays();
_builtInRegistrationService.RegisterBuiltInDataModelInputs();
_builtInRegistrationService.RegisterBuiltInPropertyEditors();
_titleUpdateTimer.Elapsed += OnTitleUpdateTimerOnElapsed;
_colorScheme.SettingChanged += ColorSchemeOnSettingChanged;
_themeWatcher.ThemeChanged += ThemeWatcherOnThemeChanged;
SidebarViewModel.PropertyChanged += SidebarViewModelOnPropertyChanged;
_titleUpdateTimer.Start();
}
protected override void OnDeactivate()
{
// Ensure no element with focus can leak, if we don't do this the root VM is retained by Window.EffectiveValues
// https://stackoverflow.com/a/30864434
Keyboard.ClearFocus();
MainMessageQueue = null;
_titleUpdateTimer.Stop();
var window = (MaterialWindow) View;
_windowSize.Value ??= new WindowSize();
_windowSize.Value.ApplyFromWindow(window);
_windowSize.Save();
_titleUpdateTimer.Elapsed -= OnTitleUpdateTimerOnElapsed;
_colorScheme.SettingChanged -= ColorSchemeOnSettingChanged;
_themeWatcher.ThemeChanged -= ThemeWatcherOnThemeChanged;
SidebarViewModel.PropertyChanged -= SidebarViewModelOnPropertyChanged;
}
protected override void OnClose()
{
SidebarViewModel.Dispose();
// Lets force the GC to run after closing the window so it is obvious to users watching task manager
// that closing the UI will decrease the memory footprint of the application.
Task.Run(async () =>
{
await Task.Delay(TimeSpan.FromSeconds(15));
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
});
}
#endregion
@@ -3,7 +3,6 @@ using System.Linq;
using System.Timers;
using Artemis.Core.Events;
using Artemis.Core.Services.Interfaces;
using Artemis.UI.Services;
using Artemis.UI.Shared.DataModelVisualization.Shared;
using Artemis.UI.Shared.Services;
using Stylet;
@@ -16,17 +15,16 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
private readonly IPluginService _pluginService;
private readonly Timer _updateTimer;
private bool _isModuleFilterEnabled;
private Core.Plugins.Abstract.Module _selectedModule;
private DataModelPropertiesViewModel _mainDataModel;
private string _propertySearch;
private List<Core.Plugins.Abstract.Module> _modules;
private string _propertySearch;
private Core.Plugins.Abstract.Module _selectedModule;
public DataModelDebugViewModel(IDataModelVisualizationService dataModelVisualizationService, IPluginService pluginService)
{
_dataModelVisualizationService = dataModelVisualizationService;
_pluginService = pluginService;
_updateTimer = new Timer(500);
_updateTimer.Elapsed += (sender, args) => MainDataModel.Update(_dataModelVisualizationService);
DisplayName = "Data model";
}
@@ -77,6 +75,7 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
{
GetDataModel();
_updateTimer.Start();
_updateTimer.Elapsed += OnUpdateTimerOnElapsed;
_pluginService.PluginEnabled += PluginServiceOnPluginToggled;
_pluginService.PluginDisabled += PluginServiceOnPluginToggled;
@@ -86,10 +85,16 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
protected override void OnDeactivate()
{
_updateTimer.Stop();
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
_pluginService.PluginEnabled -= PluginServiceOnPluginToggled;
_pluginService.PluginDisabled -= PluginServiceOnPluginToggled;
}
private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs args)
{
MainDataModel.Update(_dataModelVisualizationService);
}
private void GetDataModel()
{
MainDataModel = SelectedModule != null
@@ -19,7 +19,7 @@ using Stylet;
namespace Artemis.UI.Screens.Sidebar
{
public class SidebarViewModel : PropertyChangedBase, IHandle<RequestSelectSidebarItemEvent>
public class SidebarViewModel : PropertyChangedBase, IHandle<RequestSelectSidebarItemEvent>, IDisposable
{
private readonly IKernel _kernel;
private readonly IModuleVmFactory _moduleVmFactory;
@@ -37,10 +37,10 @@ namespace Artemis.UI.Screens.Sidebar
SidebarModules = new Dictionary<INavigationItem, Core.Plugins.Abstract.Module>();
SidebarItems = new BindableCollection<INavigationItem>();
SetupSidebar();
_pluginService.PluginEnabled += PluginServiceOnPluginEnabled;
_pluginService.PluginDisabled += PluginServiceOnPluginDisabled;
SetupSidebar();
eventAggregator.Subscribe(this);
}
@@ -64,7 +64,7 @@ namespace Artemis.UI.Screens.Sidebar
public void SetupSidebar()
{
SidebarItems.Clear();
SidebarItems.Clear();
SidebarModules.Clear();
// Add all default sidebar items
@@ -194,5 +194,11 @@ namespace Artemis.UI.Screens.Sidebar
}
#endregion
public void Dispose()
{
_pluginService.PluginEnabled -= PluginServiceOnPluginEnabled;
_pluginService.PluginDisabled -= PluginServiceOnPluginDisabled;
}
}
}
@@ -51,6 +51,8 @@ namespace Artemis.UI.Screens.Splash
_pluginService.CopyingBuildInPlugins -= OnPluginServiceOnCopyingBuildInPlugins;
_pluginService.PluginLoading -= OnPluginServiceOnPluginLoading;
_pluginService.PluginLoaded -= OnPluginServiceOnPluginLoaded;
_pluginService.PluginEnabling -= PluginServiceOnPluginEnabling;
_pluginService.PluginEnabled -= PluginServiceOnPluginEnabled;
base.OnClose();
}
@@ -12,13 +12,23 @@
<StackPanel Margin="16">
<TextBlock Text="{Binding Title}" Style="{StaticResource MaterialDesignHeadline6TextBlock}" />
<TextBlock Text="Note: These are not being validated yet" Style="{StaticResource MaterialDesignSubtitle1TextBlock}" />
<TextBox materialDesign:HintAssist.Hint="X-coordinate" Text="{Binding X}" Style="{StaticResource MaterialDesignFloatingHintTextBox}" Margin="0 10" />
<TextBox materialDesign:HintAssist.Hint="Y-coordinate" Text="{Binding Y}" Style="{StaticResource MaterialDesignFloatingHintTextBox}" Margin="0 10" />
<TextBox materialDesign:HintAssist.Hint="X-coordinate"
Text="{Binding X, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Margin="0 10" />
<TextBox materialDesign:HintAssist.Hint="Y-coordinate"
Text="{Binding Y, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Margin="0 10" />
<TextBox materialDesign:HintAssist.Hint="Scale"
Text="{Binding Scale, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Margin="0 10" />
<TextBox materialDesign:HintAssist.Hint="Rotation"
Text="{Binding Rotation, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Margin="0 10" />
<TextBox materialDesign:HintAssist.Hint="Scale" Text="{Binding Scale}" Style="{StaticResource MaterialDesignFloatingHintTextBox}" Margin="0 10" />
<TextBox materialDesign:HintAssist.Hint="Rotation" Text="{Binding Rotation}" Style="{StaticResource MaterialDesignFloatingHintTextBox}" Margin="0 10" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button Style="{StaticResource MaterialDesignFlatButton}" IsCancel="True" Margin="0 8 8 0"
Command="{s:Action Cancel}">