1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-31 17:53:32 +00:00

Shared UI - Resolved all remaining warnings

UI - Resolved all remaining warnings
Layer properties - Fixed DisableKeyframes layer property attribute not being applied
This commit is contained in:
SpoinkyNL 2020-11-20 23:13:37 +01:00
parent 931b43aa66
commit 8d901027ee
75 changed files with 810 additions and 562 deletions

View File

@ -491,6 +491,9 @@ namespace Artemis.Core
Entity = entity ?? throw new ArgumentNullException(nameof(entity)); Entity = entity ?? throw new ArgumentNullException(nameof(entity));
PropertyDescription = description ?? throw new ArgumentNullException(nameof(description)); PropertyDescription = description ?? throw new ArgumentNullException(nameof(description));
IsLoadedFromStorage = fromStorage; IsLoadedFromStorage = fromStorage;
if (PropertyDescription.DisableKeyframes)
KeyframesSupported = false;
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@ -184,7 +184,7 @@ namespace Artemis.UI.Shared
return rotationRect.Size; return rotationRect.Size;
} }
private void OnUnloaded(object sender, RoutedEventArgs e) private void OnUnloaded(object? sender, RoutedEventArgs e)
{ {
_timer.Stop(); _timer.Stop();
@ -196,12 +196,12 @@ namespace Artemis.UI.Shared
} }
} }
private void OnLoaded(object sender, RoutedEventArgs e) private void OnLoaded(object? sender, RoutedEventArgs e)
{ {
_timer.Start(); _timer.Start();
} }
private void TimerOnTick(object sender, EventArgs e) private void TimerOnTick(object? sender, EventArgs e)
{ {
if (ShowColors && Visibility == Visibility.Visible) if (ShowColors && Visibility == Visibility.Visible)
Render(); Render();

View File

@ -13,20 +13,20 @@ namespace Artemis.UI.Shared
public class ColorToStringConverter : IValueConverter public class ColorToStringConverter : IValueConverter
{ {
/// <inheritdoc /> /// <inheritdoc />
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) public object? Convert(object? value, Type targetType, object parameter, CultureInfo culture)
{ {
return value?.ToString()?.ToUpper(); return value?.ToString()?.ToUpper();
} }
/// <inheritdoc /> /// <inheritdoc />
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) public object? ConvertBack(object? value, Type targetType, object parameter, CultureInfo culture)
{ {
try try
{ {
if (string.IsNullOrWhiteSpace((string) value)) if (string.IsNullOrWhiteSpace(value as string))
return default(Color); return default(Color);
object color = ColorConverter.ConvertFromString((string) value); object? color = ColorConverter.ConvertFromString((string) value!);
if (color is Color c) if (color is Color c)
return c; return c;

View File

@ -14,18 +14,18 @@ namespace Artemis.UI.Shared
public class SKColorToStringConverter : IValueConverter public class SKColorToStringConverter : IValueConverter
{ {
/// <inheritdoc /> /// <inheritdoc />
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) public object? Convert(object? value, Type targetType, object parameter, CultureInfo culture)
{ {
return value?.ToString(); return value?.ToString();
} }
/// <inheritdoc /> /// <inheritdoc />
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) public object ConvertBack(object? value, Type targetType, object parameter, CultureInfo culture)
{ {
if (string.IsNullOrWhiteSpace((string) value)) if (string.IsNullOrWhiteSpace(value as string))
return SKColor.Empty; return SKColor.Empty;
return SKColor.TryParse((string) value, out SKColor color) ? color : SKColor.Empty; return SKColor.TryParse((string) value!, out SKColor color) ? color : SKColor.Empty;
} }
} }
} }

View File

@ -11,7 +11,7 @@ namespace Artemis.UI.Shared
public class TypeToStringConverter : IValueConverter public class TypeToStringConverter : IValueConverter
{ {
/// <inheritdoc /> /// <inheritdoc />
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) public object? Convert(object value, Type targetType, object? parameter, CultureInfo culture)
{ {
bool humanizeProvided = bool.TryParse(parameter?.ToString(), out bool humanize); bool humanizeProvided = bool.TryParse(parameter?.ToString(), out bool humanize);
if (value is Type type) if (value is Type type)

View File

@ -1,4 +1,5 @@
using Artemis.Core.DataModelExpansions; using System.Diagnostics.CodeAnalysis;
using Artemis.Core.DataModelExpansions;
using Stylet; using Stylet;
namespace Artemis.UI.Shared namespace Artemis.UI.Shared
@ -9,11 +10,13 @@ namespace Artemis.UI.Shared
/// <typeparam name="T">The type of the data model</typeparam> /// <typeparam name="T">The type of the data model</typeparam>
public abstract class DataModelDisplayViewModel<T> : DataModelDisplayViewModel public abstract class DataModelDisplayViewModel<T> : DataModelDisplayViewModel
{ {
private T _displayValue; [AllowNull]
private T _displayValue = default!;
/// <summary> /// <summary>
/// Gets or sets value that the view model must display /// Gets or sets value that the view model must display
/// </summary> /// </summary>
[AllowNull]
public T DisplayValue public T DisplayValue
{ {
get => _displayValue; get => _displayValue;
@ -24,10 +27,10 @@ namespace Artemis.UI.Shared
} }
} }
internal override object InternalGuard => null; internal override object InternalGuard => new object();
/// <inheritdoc /> /// <inheritdoc />
public override void UpdateValue(object model) public override void UpdateValue(object? model)
{ {
DisplayValue = model is T value ? value : default; DisplayValue = model is T value ? value : default;
} }
@ -45,12 +48,12 @@ namespace Artemis.UI.Shared
/// </summary> /// </summary>
public abstract class DataModelDisplayViewModel : PropertyChangedBase public abstract class DataModelDisplayViewModel : PropertyChangedBase
{ {
private DataModelPropertyAttribute _propertyDescription; private DataModelPropertyAttribute? _propertyDescription;
/// <summary> /// <summary>
/// Gets the property description of this value /// Gets the property description of this value
/// </summary> /// </summary>
public DataModelPropertyAttribute PropertyDescription public DataModelPropertyAttribute? PropertyDescription
{ {
get => _propertyDescription; get => _propertyDescription;
internal set => SetAndNotify(ref _propertyDescription, value); internal set => SetAndNotify(ref _propertyDescription, value);
@ -65,6 +68,6 @@ namespace Artemis.UI.Shared
/// Updates the display value /// Updates the display value
/// </summary> /// </summary>
/// <param name="model">The value to set</param> /// <param name="model">The value to set</param>
public abstract void UpdateValue(object model); public abstract void UpdateValue(object? model);
} }
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Data; using System.Windows.Data;
@ -16,7 +17,7 @@ namespace Artemis.UI.Shared
public abstract class DataModelInputViewModel<T> : DataModelInputViewModel public abstract class DataModelInputViewModel<T> : DataModelInputViewModel
{ {
private bool _closed; private bool _closed;
private T _inputValue; [AllowNull] private T _inputValue = default!;
/// <summary> /// <summary>
/// Creates a new instance of the <see cref="DataModelInputViewModel{T}" /> class /// Creates a new instance of the <see cref="DataModelInputViewModel{T}" /> class
@ -32,6 +33,7 @@ namespace Artemis.UI.Shared
/// <summary> /// <summary>
/// Gets or sets the value shown in the input /// Gets or sets the value shown in the input
/// </summary> /// </summary>
[AllowNull]
public T InputValue public T InputValue
{ {
get => _inputValue; get => _inputValue;
@ -82,13 +84,13 @@ namespace Artemis.UI.Shared
// ReSharper disable once UnusedMember.Global // ReSharper disable once UnusedMember.Global
internal abstract object InternalGuard { get; } internal abstract object InternalGuard { get; }
internal Action<object, bool> UpdateCallback { get; set; } internal Action<object?, bool> UpdateCallback { get; set; } = null!; // Set right after construction
/// <summary> /// <summary>
/// Gets the types this input view model can support through type conversion. This list is defined when registering the /// Gets the types this input view model can support through type conversion. This list is defined when registering the
/// view model. /// view model.
/// </summary> /// </summary>
internal IReadOnlyCollection<Type> CompatibleConversionTypes { get; set; } internal IReadOnlyCollection<Type>? CompatibleConversionTypes { get; set; }
/// <summary> /// <summary>
/// Submits the input value and removes this view model. /// Submits the input value and removes this view model.
@ -133,6 +135,6 @@ namespace Artemis.UI.Shared
} }
/// <inheritdoc /> /// <inheritdoc />
public UIElement View { get; set; } public UIElement? View { get; set; }
} }
} }

View File

@ -12,21 +12,25 @@ using Stylet;
namespace Artemis.UI.Shared.Input namespace Artemis.UI.Shared.Input
{ {
/// <summary>
/// Represents a view model that allows selecting a data model property used by boolean operations on a certain data
/// model property
/// </summary>
public class DataModelDynamicViewModel : PropertyChangedBase, IDisposable public class DataModelDynamicViewModel : PropertyChangedBase, IDisposable
{ {
private readonly IDataModelUIService _dataModelUIService; private readonly IDataModelUIService _dataModelUIService;
private readonly Module _module; private readonly Module _module;
private readonly Timer _updateTimer; private readonly Timer _updateTimer;
private SolidColorBrush _buttonBrush = new SolidColorBrush(Color.FromRgb(171, 71, 188)); private SolidColorBrush _buttonBrush = new SolidColorBrush(Color.FromRgb(171, 71, 188));
private DataModelPath _dataModelPath; private DataModelPath? _dataModelPath;
private DataModelPropertiesViewModel _dataModelViewModel; private DataModelPropertiesViewModel? _dataModelViewModel;
private bool _displaySwitchButton; private bool _displaySwitchButton;
private Type[] _filterTypes; private Type[] _filterTypes = new Type[0];
private bool _isDataModelViewModelOpen; private bool _isDataModelViewModelOpen;
private bool _isEnabled = true; private bool _isEnabled = true;
private string _placeholder = "Select a property"; private string _placeholder = "Select a property";
public DataModelDynamicViewModel(Module module, ISettingsService settingsService, IDataModelUIService dataModelUIService) internal DataModelDynamicViewModel(Module module, ISettingsService settingsService, IDataModelUIService dataModelUIService)
{ {
_module = module; _module = module;
_dataModelUIService = dataModelUIService; _dataModelUIService = dataModelUIService;
@ -39,6 +43,9 @@ namespace Artemis.UI.Shared.Input
Initialize(); Initialize();
} }
/// <summary>
/// Gets or sets the brush to use for the input button
/// </summary>
public SolidColorBrush ButtonBrush public SolidColorBrush ButtonBrush
{ {
get => _buttonBrush; get => _buttonBrush;
@ -49,26 +56,41 @@ namespace Artemis.UI.Shared.Input
} }
} }
/// <summary>
/// Gets the brush to use for the switch button
/// </summary>
public SolidColorBrush SwitchButtonBrush => new SolidColorBrush(ButtonBrush.Color.Darken()); public SolidColorBrush SwitchButtonBrush => new SolidColorBrush(ButtonBrush.Color.Darken());
/// <summary>
/// Gets or sets the placeholder text when no value is entered
/// </summary>
public string Placeholder public string Placeholder
{ {
get => _placeholder; get => _placeholder;
set => SetAndNotify(ref _placeholder, value); set => SetAndNotify(ref _placeholder, value);
} }
/// <summary>
/// Gets or sets the enabled state of the input
/// </summary>
public bool IsEnabled public bool IsEnabled
{ {
get => _isEnabled; get => _isEnabled;
set => SetAndNotify(ref _isEnabled, value); set => SetAndNotify(ref _isEnabled, value);
} }
/// <summary>
/// Gets or sets whether the switch button should be displayed
/// </summary>
public bool DisplaySwitchButton public bool DisplaySwitchButton
{ {
get => _displaySwitchButton; get => _displaySwitchButton;
set => SetAndNotify(ref _displaySwitchButton, value); set => SetAndNotify(ref _displaySwitchButton, value);
} }
/// <summary>
/// Gets or sets the types of properties this view model will allow to be selected
/// </summary>
public Type[] FilterTypes public Type[] FilterTypes
{ {
get => _filterTypes; get => _filterTypes;
@ -79,18 +101,38 @@ namespace Artemis.UI.Shared.Input
} }
} }
/// <summary>
/// Gets a bindable collection of extra data model view models to show
/// </summary>
public BindableCollection<DataModelPropertiesViewModel> ExtraDataModelViewModels { get; } public BindableCollection<DataModelPropertiesViewModel> ExtraDataModelViewModels { get; }
/// <summary>
/// Gets a boolean indicating whether there are any extra data models
/// </summary>
public bool HasExtraDataModels => ExtraDataModelViewModels.Any(); public bool HasExtraDataModels => ExtraDataModelViewModels.Any();
/// <summary>
/// Command used by view
/// </summary>
public DelegateCommand SelectPropertyCommand { get; } public DelegateCommand SelectPropertyCommand { get; }
/// <summary>
/// Setting used by view
/// </summary>
public PluginSetting<bool> ShowDataModelValues { get; } public PluginSetting<bool> ShowDataModelValues { get; }
public DataModelPropertiesViewModel DataModelViewModel /// <summary>
/// Gets or sets root the data model view model
/// </summary>
public DataModelPropertiesViewModel? DataModelViewModel
{ {
get => _dataModelViewModel; get => _dataModelViewModel;
private set => SetAndNotify(ref _dataModelViewModel, value); private set => SetAndNotify(ref _dataModelViewModel, value);
} }
/// <summary>
/// Gets or sets a boolean indicating whether the data model is open
/// </summary>
public bool IsDataModelViewModelOpen public bool IsDataModelViewModelOpen
{ {
get => _isDataModelViewModelOpen; get => _isDataModelViewModelOpen;
@ -101,7 +143,10 @@ namespace Artemis.UI.Shared.Input
} }
} }
public DataModelPath DataModelPath /// <summary>
/// Gets or sets the data model path of the currently selected data model property
/// </summary>
public DataModelPath? DataModelPath
{ {
get => _dataModelPath; get => _dataModelPath;
private set private set
@ -113,9 +158,19 @@ namespace Artemis.UI.Shared.Input
} }
} }
/// <summary>
/// Gets a boolean indicating whether the current selection is valid
/// </summary>
public bool IsValid => DataModelPath?.IsValid ?? true; public bool IsValid => DataModelPath?.IsValid ?? true;
public string DisplayValue => DataModelPath?.GetPropertyDescription()?.Name ?? DataModelPath?.Segments.LastOrDefault()?.Identifier;
/// <summary>
/// Gets the display name of the currently selected property
/// </summary>
public string? DisplayValue => DataModelPath?.GetPropertyDescription()?.Name ?? DataModelPath?.Segments.LastOrDefault()?.Identifier;
/// <summary>
/// Gets the human readable path of the currently selected property
/// </summary>
public string DisplayPath public string DisplayPath
{ {
get get
@ -128,8 +183,16 @@ namespace Artemis.UI.Shared.Input
} }
} }
/// <summary>
/// Gets or sets a boolean indicating whether event child VMs should be loaded, defaults to <see langword="true" />
/// </summary>
public bool LoadEventChildren { get; set; } = true; public bool LoadEventChildren { get; set; } = true;
/// <summary>
/// Changes the root data model VM stored in <see cref="DataModelViewModel" /> to the provided
/// <paramref name="dataModel" />
/// </summary>
/// <param name="dataModel">The data model VM to set the new root data model to</param>
public void ChangeDataModel(DataModelPropertiesViewModel dataModel) public void ChangeDataModel(DataModelPropertiesViewModel dataModel)
{ {
if (DataModelViewModel != null) if (DataModelViewModel != null)
@ -141,12 +204,19 @@ namespace Artemis.UI.Shared.Input
DataModelViewModel.UpdateRequested += DataModelOnUpdateRequested; DataModelViewModel.UpdateRequested += DataModelOnUpdateRequested;
} }
public void ChangeDataModelPath(DataModelPath dataModelPath) /// <summary>
/// Changes the currently selected property by its path
/// </summary>
/// <param name="dataModelPath">The path of the property to set the selection to</param>
public void ChangeDataModelPath(DataModelPath? dataModelPath)
{ {
DataModelPath?.Dispose(); DataModelPath?.Dispose();
DataModelPath = dataModelPath != null ? new DataModelPath(dataModelPath) : null; DataModelPath = dataModelPath != null ? new DataModelPath(dataModelPath) : null;
} }
/// <summary>
/// Requests switching the input type to static using a <see cref="DataModelStaticViewModel" />
/// </summary>
public void SwitchToStatic() public void SwitchToStatic()
{ {
ChangeDataModelPath(null); ChangeDataModelPath(null);
@ -158,13 +228,14 @@ namespace Artemis.UI.Shared.Input
{ {
// Get the data models // Get the data models
DataModelViewModel = _dataModelUIService.GetPluginDataModelVisualization(_module, true); DataModelViewModel = _dataModelUIService.GetPluginDataModelVisualization(_module, true);
DataModelViewModel.UpdateRequested += DataModelOnUpdateRequested; if (DataModelViewModel != null)
DataModelViewModel.UpdateRequested += DataModelOnUpdateRequested;
ExtraDataModelViewModels.CollectionChanged += ExtraDataModelViewModelsOnCollectionChanged; ExtraDataModelViewModels.CollectionChanged += ExtraDataModelViewModelsOnCollectionChanged;
_updateTimer.Start(); _updateTimer.Start();
_updateTimer.Elapsed += OnUpdateTimerOnElapsed; _updateTimer.Elapsed += OnUpdateTimerOnElapsed;
} }
private void ExecuteSelectPropertyCommand(object context) private void ExecuteSelectPropertyCommand(object? context)
{ {
if (!(context is DataModelVisualizationViewModel selected)) if (!(context is DataModelVisualizationViewModel selected))
return; return;
@ -173,40 +244,61 @@ namespace Artemis.UI.Shared.Input
OnPropertySelected(new DataModelInputDynamicEventArgs(DataModelPath)); OnPropertySelected(new DataModelInputDynamicEventArgs(DataModelPath));
} }
#region IDisposable
/// <summary>
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_updateTimer.Stop();
_updateTimer.Dispose();
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
DataModelPath?.Dispose();
}
}
/// <inheritdoc />
public void Dispose() public void Dispose()
{ {
_updateTimer.Stop(); Dispose(true);
_updateTimer.Dispose(); GC.SuppressFinalize(this);
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
DataModelPath?.Dispose();
} }
#endregion
#region Event handlers #region Event handlers
private void DataModelOnUpdateRequested(object sender, EventArgs e) private void DataModelOnUpdateRequested(object? sender, EventArgs e)
{ {
DataModelViewModel.ApplyTypeFilter(true, FilterTypes); DataModelViewModel?.ApplyTypeFilter(true, FilterTypes);
foreach (DataModelPropertiesViewModel extraDataModelViewModel in ExtraDataModelViewModels) foreach (DataModelPropertiesViewModel extraDataModelViewModel in ExtraDataModelViewModels)
extraDataModelViewModel.ApplyTypeFilter(true, FilterTypes); extraDataModelViewModel.ApplyTypeFilter(true, FilterTypes);
} }
private void ExtraDataModelViewModelsOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) private void ExtraDataModelViewModelsOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{ {
NotifyOfPropertyChange(nameof(HasExtraDataModels)); NotifyOfPropertyChange(nameof(HasExtraDataModels));
} }
private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e) private void OnUpdateTimerOnElapsed(object? sender, ElapsedEventArgs e)
{ {
if (!IsDataModelViewModelOpen) if (!IsDataModelViewModelOpen)
return; return;
UpdateDataModelVisualization(); UpdateDataModelVisualization();
} }
private void UpdateDataModelVisualization() private void UpdateDataModelVisualization()
{ {
DataModelViewModel.Update(_dataModelUIService, new DataModelUpdateConfiguration(LoadEventChildren)); DataModelViewModel?.Update(_dataModelUIService, new DataModelUpdateConfiguration(LoadEventChildren));
foreach (DataModelPropertiesViewModel extraDataModelViewModel in ExtraDataModelViewModels) foreach (DataModelPropertiesViewModel extraDataModelViewModel in ExtraDataModelViewModels)
extraDataModelViewModel.Update(_dataModelUIService, new DataModelUpdateConfiguration(LoadEventChildren)); extraDataModelViewModel.Update(_dataModelUIService, new DataModelUpdateConfiguration(LoadEventChildren));
} }
@ -215,14 +307,28 @@ namespace Artemis.UI.Shared.Input
#region Events #region Events
public event EventHandler<DataModelInputDynamicEventArgs> PropertySelected; /// <summary>
public event EventHandler SwitchToStaticRequested; /// Occurs when anew property has been selected
/// </summary>
public event EventHandler<DataModelInputDynamicEventArgs>? PropertySelected;
/// <summary>
/// Occurs when a switch to static input has been requested
/// </summary>
public event EventHandler? SwitchToStaticRequested;
/// <summary>
/// Invokes the <see cref="PropertySelected" /> event
/// </summary>
/// <param name="e"></param>
protected virtual void OnPropertySelected(DataModelInputDynamicEventArgs e) protected virtual void OnPropertySelected(DataModelInputDynamicEventArgs e)
{ {
PropertySelected?.Invoke(this, e); PropertySelected?.Invoke(this, e);
} }
/// <summary>
/// Invokes the <see cref="SwitchToStaticRequested" /> event
/// </summary>
protected virtual void OnSwitchToStaticRequested() protected virtual void OnSwitchToStaticRequested()
{ {
SwitchToStaticRequested?.Invoke(this, EventArgs.Empty); SwitchToStaticRequested?.Invoke(this, EventArgs.Empty);

View File

@ -11,29 +11,33 @@ using Stylet;
namespace Artemis.UI.Shared.Input namespace Artemis.UI.Shared.Input
{ {
/// <summary>
/// Represents a view model that allows inputting a static value used by boolean operations on a certain data model
/// property
/// </summary>
public class DataModelStaticViewModel : PropertyChangedBase, IDisposable public class DataModelStaticViewModel : PropertyChangedBase, IDisposable
{ {
private readonly IDataModelUIService _dataModelUIService; private readonly IDataModelUIService _dataModelUIService;
private readonly Window _rootView; private readonly Window? _rootView;
private SolidColorBrush _buttonBrush = new SolidColorBrush(Color.FromRgb(171, 71, 188)); private SolidColorBrush _buttonBrush = new SolidColorBrush(Color.FromRgb(171, 71, 188));
private bool _displaySwitchButton; private bool _displaySwitchButton;
private DataModelDisplayViewModel _displayViewModel; private DataModelDisplayViewModel? _displayViewModel;
private DataModelInputViewModel _inputViewModel; private DataModelInputViewModel? _inputViewModel;
private bool _isEnabled; private bool _isEnabled;
private string _placeholder = "Enter a value"; private string _placeholder = "Enter a value";
private DataModelPropertyAttribute _targetDescription; private DataModelPropertyAttribute _targetDescription;
private Type _targetType; private Type _targetType;
private object _value; private object? _value;
public DataModelStaticViewModel(Type targetType, DataModelPropertyAttribute targetDescription, IDataModelUIService dataModelUIService) internal DataModelStaticViewModel(Type targetType, DataModelPropertyAttribute targetDescription, IDataModelUIService dataModelUIService)
{ {
_dataModelUIService = dataModelUIService; _dataModelUIService = dataModelUIService;
_rootView = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive); _rootView = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
TargetType = targetType; _targetType = targetType;
TargetDescription = targetDescription; _targetDescription = targetDescription;
IsEnabled = TargetType != null; _isEnabled = TargetType != null;
DisplayViewModel = _dataModelUIService.GetDataModelDisplayViewModel(TargetType ?? typeof(object), TargetDescription, true); _displayViewModel = _dataModelUIService.GetDataModelDisplayViewModel(TargetType ?? typeof(object), TargetDescription, true);
if (_rootView != null) if (_rootView != null)
{ {
@ -63,7 +67,7 @@ namespace Artemis.UI.Shared.Input
/// <summary> /// <summary>
/// Gets the view model used to display the value /// Gets the view model used to display the value
/// </summary> /// </summary>
public DataModelDisplayViewModel DisplayViewModel public DataModelDisplayViewModel? DisplayViewModel
{ {
get => _displayViewModel; get => _displayViewModel;
private set => SetAndNotify(ref _displayViewModel, value); private set => SetAndNotify(ref _displayViewModel, value);
@ -72,7 +76,7 @@ namespace Artemis.UI.Shared.Input
/// <summary> /// <summary>
/// Gets the view model used to edit the value /// Gets the view model used to edit the value
/// </summary> /// </summary>
public DataModelInputViewModel InputViewModel public DataModelInputViewModel? InputViewModel
{ {
get => _inputViewModel; get => _inputViewModel;
private set => SetAndNotify(ref _inputViewModel, value); private set => SetAndNotify(ref _inputViewModel, value);
@ -99,7 +103,7 @@ namespace Artemis.UI.Shared.Input
/// <summary> /// <summary>
/// Gets or sets the value of the target /// Gets or sets the value of the target
/// </summary> /// </summary>
public object Value public object? Value
{ {
get => _value; get => _value;
set set
@ -173,7 +177,7 @@ namespace Artemis.UI.Shared.Input
} }
/// <summary> /// <summary>
/// Requests switching the input type to dynamic /// Requests switching the input type to dynamic using a <see cref="DataModelDynamicViewModel"/>
/// </summary> /// </summary>
public void SwitchToDynamic() public void SwitchToDynamic()
{ {
@ -183,7 +187,7 @@ namespace Artemis.UI.Shared.Input
OnSwitchToDynamicRequested(); OnSwitchToDynamicRequested();
} }
private void ApplyFreeInput(object value, bool submitted) private void ApplyFreeInput(object? value, bool submitted)
{ {
if (submitted) if (submitted)
OnValueUpdated(new DataModelInputStaticEventArgs(value)); OnValueUpdated(new DataModelInputStaticEventArgs(value));
@ -204,11 +208,13 @@ namespace Artemis.UI.Shared.Input
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (disposing) if (disposing)
{
if (_rootView != null) if (_rootView != null)
{ {
_rootView.MouseUp -= RootViewOnMouseUp; _rootView.MouseUp -= RootViewOnMouseUp;
_rootView.KeyUp -= RootViewOnKeyUp; _rootView.KeyUp -= RootViewOnKeyUp;
} }
}
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@ -6,21 +6,28 @@ using Artemis.UI.Shared.Services;
namespace Artemis.UI.Shared namespace Artemis.UI.Shared
{ {
/// <summary>
/// Represents a view model that visualizes an event data model property
/// </summary>
public class DataModelEventViewModel : DataModelVisualizationViewModel public class DataModelEventViewModel : DataModelVisualizationViewModel
{ {
private Type _displayValueType; private Type? _displayValueType;
internal DataModelEventViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, DataModelPath dataModelPath) : base(dataModel, parent, dataModelPath) internal DataModelEventViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, DataModelPath dataModelPath) : base(dataModel, parent, dataModelPath)
{ {
} }
public Type DisplayValueType /// <summary>
/// Gets the type of event arguments this event triggers and that must be displayed as children
/// </summary>
public Type? DisplayValueType
{ {
get => _displayValueType; get => _displayValueType;
set => SetAndNotify(ref _displayValueType, value); set => SetAndNotify(ref _displayValueType, value);
} }
public override void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration configuration) /// <inheritdoc />
public override void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? configuration)
{ {
DisplayValueType = DataModelPath?.GetPropertyType(); DisplayValueType = DataModelPath?.GetPropertyType();
@ -40,13 +47,16 @@ namespace Artemis.UI.Shared
dataModelVisualizationViewModel.Update(dataModelUIService, configuration); dataModelVisualizationViewModel.Update(dataModelUIService, configuration);
} }
public override object GetCurrentValue() /// <summary>
/// Always returns <see langword="null"/> for data model events
/// </summary>
public override object? GetCurrentValue()
{ {
return null; return null;
} }
/// <inheritdoc /> /// <inheritdoc />
public override string ToString() public override string? ToString()
{ {
return DisplayPath ?? Path; return DisplayPath ?? Path;
} }

View File

@ -5,43 +5,63 @@ using Artemis.UI.Shared.Services;
namespace Artemis.UI.Shared namespace Artemis.UI.Shared
{ {
/// <summary>
/// Represents a view model that wraps a regular <see cref="DataModelPropertiesViewModel" /> but contained in
/// a <see cref="DataModelListViewModel" />
/// </summary>
public class DataModelListPropertiesViewModel : DataModelPropertiesViewModel public class DataModelListPropertiesViewModel : DataModelPropertiesViewModel
{ {
private object _displayValue; private readonly ListPredicateWrapperDataModel _listPredicateWrapper;
private object? _displayValue;
private int _index; private int _index;
private Type _listType; private Type? _listType;
public DataModelListPropertiesViewModel(Type listType) : base(null, null, null) internal DataModelListPropertiesViewModel(Type listType) : base(null, null, null)
{ {
DataModel = ListPredicateWrapperDataModel.Create(listType); _listPredicateWrapper = ListPredicateWrapperDataModel.Create(listType);
DataModel = _listPredicateWrapper;
ListType = listType; ListType = listType;
} }
/// <summary>
/// Gets the index of the element within the list
/// </summary>
public int Index public int Index
{ {
get => _index; get => _index;
set => SetAndNotify(ref _index, value); set => SetAndNotify(ref _index, value);
} }
public Type ListType /// <summary>
/// Gets the type of elements contained in the list
/// </summary>
public Type? ListType
{ {
get => _listType; get => _listType;
set => SetAndNotify(ref _listType, value); set => SetAndNotify(ref _listType, value);
} }
public new object DisplayValue /// <summary>
/// Gets the value of the property that is being visualized
/// </summary>
public new object? DisplayValue
{ {
get => _displayValue; get => _displayValue;
set => SetAndNotify(ref _displayValue, value); set => SetAndNotify(ref _displayValue, value);
} }
public DataModelVisualizationViewModel DisplayViewModel => Children.FirstOrDefault(); /// <summary>
/// Gets the view model that handles displaying the property
/// </summary>
public DataModelVisualizationViewModel? DisplayViewModel => Children.FirstOrDefault();
public override string DisplayPath => null; /// <inheritdoc />
public override string? DisplayPath => null;
public override void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration configuration) /// <inheritdoc />
public override void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? configuration)
{ {
((ListPredicateWrapperDataModel) DataModel).UntypedValue = DisplayValue; _listPredicateWrapper.UntypedValue = DisplayValue;
PopulateProperties(dataModelUIService, configuration); PopulateProperties(dataModelUIService, configuration);
if (DisplayViewModel == null) if (DisplayViewModel == null)
@ -52,7 +72,8 @@ namespace Artemis.UI.Shared
DisplayViewModel.Update(dataModelUIService, null); DisplayViewModel.Update(dataModelUIService, null);
} }
public override object GetCurrentValue() /// <inheritdoc />
public override object? GetCurrentValue()
{ {
return DisplayValue; return DisplayValue;
} }

View File

@ -10,19 +10,22 @@ namespace Artemis.UI.Shared
/// </summary> /// </summary>
public class DataModelListPropertyViewModel : DataModelPropertyViewModel public class DataModelListPropertyViewModel : DataModelPropertyViewModel
{ {
private readonly ListPredicateWrapperDataModel _listPredicateWrapper;
private int _index; private int _index;
private Type _listType; private Type? _listType;
internal DataModelListPropertyViewModel(Type listType, DataModelDisplayViewModel displayViewModel) : base(null, null, null) internal DataModelListPropertyViewModel(Type listType, DataModelDisplayViewModel displayViewModel) : base(null, null, null)
{ {
DataModel = ListPredicateWrapperDataModel.Create(listType); _listPredicateWrapper = ListPredicateWrapperDataModel.Create(listType);
DataModel = _listPredicateWrapper;
ListType = listType; ListType = listType;
DisplayViewModel = displayViewModel; DisplayViewModel = displayViewModel;
} }
internal DataModelListPropertyViewModel(Type listType) : base(null, null, null) internal DataModelListPropertyViewModel(Type listType) : base(null, null, null)
{ {
DataModel = ListPredicateWrapperDataModel.Create(listType); _listPredicateWrapper = ListPredicateWrapperDataModel.Create(listType);
DataModel = _listPredicateWrapper;
ListType = listType; ListType = listType;
} }
@ -38,26 +41,26 @@ namespace Artemis.UI.Shared
/// <summary> /// <summary>
/// Gets the type of elements contained in the list /// Gets the type of elements contained in the list
/// </summary> /// </summary>
public Type ListType public Type? ListType
{ {
get => _listType; get => _listType;
private set => SetAndNotify(ref _listType, value); private set => SetAndNotify(ref _listType, value);
} }
/// <inheritdoc /> /// <inheritdoc />
public override object GetCurrentValue() public override object? GetCurrentValue()
{ {
return DisplayValue; return DisplayValue;
} }
/// <inheritdoc /> /// <inheritdoc />
public override void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration configuration) public override void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? configuration)
{ {
// Display value gets updated by parent, don't do anything if it is null // Display value gets updated by parent, don't do anything if it is null
if (DisplayValue == null) if (DisplayValue == null)
return; return;
((ListPredicateWrapperDataModel) DataModel).UntypedValue = DisplayValue; _listPredicateWrapper.UntypedValue = DisplayValue;
if (DisplayViewModel == null) if (DisplayViewModel == null)
DisplayViewModel = dataModelUIService.GetDataModelDisplayViewModel(DisplayValue.GetType(), PropertyDescription, true); DisplayViewModel = dataModelUIService.GetDataModelDisplayViewModel(DisplayValue.GetType(), PropertyDescription, true);

View File

@ -13,21 +13,22 @@ namespace Artemis.UI.Shared
public class DataModelListViewModel : DataModelVisualizationViewModel public class DataModelListViewModel : DataModelVisualizationViewModel
{ {
private string _countDisplay; private string _countDisplay;
private Type _displayValueType; private Type? _displayValueType;
private IEnumerable _list; private IEnumerable? _list;
private BindableCollection<DataModelVisualizationViewModel> _listChildren; private BindableCollection<DataModelVisualizationViewModel> _listChildren;
private int _listCount; private int _listCount;
internal DataModelListViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, DataModelPath dataModelPath) internal DataModelListViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, DataModelPath dataModelPath)
: base(dataModel, parent, dataModelPath) : base(dataModel, parent, dataModelPath)
{ {
ListChildren = new BindableCollection<DataModelVisualizationViewModel>(); _countDisplay = "0 items";
_listChildren = new BindableCollection<DataModelVisualizationViewModel>();
} }
/// <summary> /// <summary>
/// Gets the instance of the list that is being visualized /// Gets the instance of the list that is being visualized
/// </summary> /// </summary>
public IEnumerable List public IEnumerable? List
{ {
get => _list; get => _list;
private set => SetAndNotify(ref _list, value); private set => SetAndNotify(ref _list, value);
@ -45,7 +46,7 @@ namespace Artemis.UI.Shared
/// <summary> /// <summary>
/// Gets the type of elements this list contains and that must be displayed as children /// Gets the type of elements this list contains and that must be displayed as children
/// </summary> /// </summary>
public Type DisplayValueType public Type? DisplayValueType
{ {
get => _displayValueType; get => _displayValueType;
set => SetAndNotify(ref _displayValueType, value); set => SetAndNotify(ref _displayValueType, value);
@ -70,7 +71,7 @@ namespace Artemis.UI.Shared
} }
/// <inheritdoc /> /// <inheritdoc />
public override void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration configuration) public override void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? configuration)
{ {
if (Parent != null && !Parent.IsVisualizationExpanded) if (Parent != null && !Parent.IsVisualizationExpanded)
return; return;
@ -86,10 +87,12 @@ namespace Artemis.UI.Shared
if (item == null) if (item == null)
continue; continue;
DataModelVisualizationViewModel child; DataModelVisualizationViewModel? child;
if (ListChildren.Count <= index) if (ListChildren.Count <= index)
{ {
child = CreateListChild(dataModelUIService, item.GetType()); child = CreateListChild(dataModelUIService, item.GetType());
if (child == null)
continue;
ListChildren.Add(child); ListChildren.Add(child);
} }
else else
@ -126,10 +129,10 @@ namespace Artemis.UI.Shared
return $"[List] {DisplayPath ?? Path} - {ListCount} item(s)"; return $"[List] {DisplayPath ?? Path} - {ListCount} item(s)";
} }
private DataModelVisualizationViewModel CreateListChild(IDataModelUIService dataModelUIService, Type listType) private DataModelVisualizationViewModel? CreateListChild(IDataModelUIService dataModelUIService, Type listType)
{ {
// If a display VM was found, prefer to use that in any case // If a display VM was found, prefer to use that in any case
DataModelDisplayViewModel typeViewModel = dataModelUIService.GetDataModelDisplayViewModel(listType, PropertyDescription); DataModelDisplayViewModel? typeViewModel = dataModelUIService.GetDataModelDisplayViewModel(listType, PropertyDescription);
if (typeViewModel != null) if (typeViewModel != null)
return new DataModelListPropertyViewModel(listType, typeViewModel); return new DataModelListPropertyViewModel(listType, typeViewModel);
// For primitives, create a property view model, it may be null that is fine // For primitives, create a property view model, it may be null that is fine

View File

@ -10,10 +10,10 @@ namespace Artemis.UI.Shared
/// </summary> /// </summary>
public class DataModelPropertiesViewModel : DataModelVisualizationViewModel public class DataModelPropertiesViewModel : DataModelVisualizationViewModel
{ {
private object _displayValue; private object? _displayValue;
private Type _displayValueType; private Type? _displayValueType;
internal DataModelPropertiesViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, DataModelPath dataModelPath) internal DataModelPropertiesViewModel(DataModel? dataModel, DataModelVisualizationViewModel? parent, DataModelPath? dataModelPath)
: base(dataModel, parent, dataModelPath) : base(dataModel, parent, dataModelPath)
{ {
} }
@ -21,7 +21,7 @@ namespace Artemis.UI.Shared
/// <summary> /// <summary>
/// Gets the type of the property that is being visualized /// Gets the type of the property that is being visualized
/// </summary> /// </summary>
public Type DisplayValueType public Type? DisplayValueType
{ {
get => _displayValueType; get => _displayValueType;
private set => SetAndNotify(ref _displayValueType, value); private set => SetAndNotify(ref _displayValueType, value);
@ -30,19 +30,19 @@ namespace Artemis.UI.Shared
/// <summary> /// <summary>
/// Gets the value of the property that is being visualized /// Gets the value of the property that is being visualized
/// </summary> /// </summary>
public object DisplayValue public object? DisplayValue
{ {
get => _displayValue; get => _displayValue;
private set => SetAndNotify(ref _displayValue, value); private set => SetAndNotify(ref _displayValue, value);
} }
/// <inheritdoc /> /// <inheritdoc />
public override void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration configuration) public override void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? configuration)
{ {
DisplayValueType = DataModelPath?.GetPropertyType(); DisplayValueType = DataModelPath?.GetPropertyType();
// Only set a display value if ToString returns useful information and not just the type name // Only set a display value if ToString returns useful information and not just the type name
object currentValue = GetCurrentValue(); object? currentValue = GetCurrentValue();
if (currentValue != null && currentValue.ToString() != currentValue.GetType().ToString()) if (currentValue != null && currentValue.ToString() != currentValue.GetType().ToString())
DisplayValue = currentValue.ToString(); DisplayValue = currentValue.ToString();
else else
@ -60,7 +60,7 @@ namespace Artemis.UI.Shared
} }
/// <inheritdoc /> /// <inheritdoc />
public override object GetCurrentValue() public override object? GetCurrentValue()
{ {
if (Parent == null || Parent.IsRootViewModel || IsRootViewModel) if (Parent == null || Parent.IsRootViewModel || IsRootViewModel)
return DataModel; return DataModel;
@ -68,7 +68,7 @@ namespace Artemis.UI.Shared
} }
/// <inheritdoc /> /// <inheritdoc />
public override string ToString() public override string? ToString()
{ {
return DisplayPath ?? Path; return DisplayPath ?? Path;
} }

View File

@ -11,11 +11,11 @@ namespace Artemis.UI.Shared
/// </summary> /// </summary>
public class DataModelPropertyViewModel : DataModelVisualizationViewModel public class DataModelPropertyViewModel : DataModelVisualizationViewModel
{ {
private object _displayValue; private object? _displayValue;
private Type _displayValueType; private Type? _displayValueType;
private DataModelDisplayViewModel _displayViewModel; private DataModelDisplayViewModel? _displayViewModel;
internal DataModelPropertyViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, DataModelPath dataModelPath) internal DataModelPropertyViewModel(DataModel? dataModel, DataModelVisualizationViewModel? parent, DataModelPath? dataModelPath)
: base(dataModel, parent, dataModelPath) : base(dataModel, parent, dataModelPath)
{ {
} }
@ -23,7 +23,7 @@ namespace Artemis.UI.Shared
/// <summary> /// <summary>
/// Gets the value of the property that is being visualized /// Gets the value of the property that is being visualized
/// </summary> /// </summary>
public object DisplayValue public object? DisplayValue
{ {
get => _displayValue; get => _displayValue;
internal set => SetAndNotify(ref _displayValue, value); internal set => SetAndNotify(ref _displayValue, value);
@ -32,7 +32,7 @@ namespace Artemis.UI.Shared
/// <summary> /// <summary>
/// Gets the type of the property that is being visualized /// Gets the type of the property that is being visualized
/// </summary> /// </summary>
public Type DisplayValueType public Type? DisplayValueType
{ {
get => _displayValueType; get => _displayValueType;
protected set => SetAndNotify(ref _displayValueType, value); protected set => SetAndNotify(ref _displayValueType, value);
@ -41,26 +41,31 @@ namespace Artemis.UI.Shared
/// <summary> /// <summary>
/// Gets the view model used to display the display value /// Gets the view model used to display the display value
/// </summary> /// </summary>
public DataModelDisplayViewModel DisplayViewModel public DataModelDisplayViewModel? DisplayViewModel
{ {
get => _displayViewModel; get => _displayViewModel;
internal set => SetAndNotify(ref _displayViewModel, value); internal set => SetAndNotify(ref _displayViewModel, value);
} }
/// <inheritdoc /> /// <inheritdoc />
public override void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration configuration) public override void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? configuration)
{ {
if (Parent != null && !Parent.IsVisualizationExpanded && !Parent.IsRootViewModel) if (Parent != null && !Parent.IsVisualizationExpanded && !Parent.IsRootViewModel)
return; return;
if (DisplayViewModel == null) if (DisplayViewModel == null)
{ {
DisplayViewModel = dataModelUIService.GetDataModelDisplayViewModel(DataModelPath.GetPropertyType(), PropertyDescription, true); Type? propertyType = DataModelPath?.GetPropertyType();
DisplayViewModel.PropertyDescription = DataModelPath.GetPropertyDescription(); if (propertyType != null)
{
DisplayViewModel = dataModelUIService.GetDataModelDisplayViewModel(propertyType, PropertyDescription, true);
if (DisplayViewModel != null)
DisplayViewModel.PropertyDescription = DataModelPath?.GetPropertyDescription();
}
} }
DisplayValue = GetCurrentValue(); DisplayValue = GetCurrentValue();
DisplayValueType = DisplayValue != null ? DisplayValue.GetType() : DataModelPath.GetPropertyType(); DisplayValueType = DisplayValue != null ? DisplayValue.GetType() : DataModelPath?.GetPropertyType();
DisplayViewModel?.UpdateValue(DisplayValue); DisplayViewModel?.UpdateValue(DisplayValue);
} }

View File

@ -17,24 +17,24 @@ namespace Artemis.UI.Shared
{ {
private const int MaxDepth = 4; private const int MaxDepth = 4;
private BindableCollection<DataModelVisualizationViewModel> _children; private BindableCollection<DataModelVisualizationViewModel> _children;
private DataModel _dataModel; private DataModel? _dataModel;
private bool _isMatchingFilteredTypes; private bool _isMatchingFilteredTypes;
private bool _isVisualizationExpanded; private bool _isVisualizationExpanded;
private DataModelVisualizationViewModel _parent; private DataModelVisualizationViewModel? _parent;
private DataModelPropertyAttribute _propertyDescription; private DataModelPropertyAttribute? _propertyDescription;
internal DataModelVisualizationViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, DataModelPath dataModelPath) internal DataModelVisualizationViewModel(DataModel? dataModel, DataModelVisualizationViewModel? parent, DataModelPath? dataModelPath)
{ {
DataModel = dataModel; _dataModel = dataModel;
Parent = parent; _children = new BindableCollection<DataModelVisualizationViewModel>();
_parent = parent;
DataModelPath = dataModelPath; DataModelPath = dataModelPath;
Children = new BindableCollection<DataModelVisualizationViewModel>();
IsMatchingFilteredTypes = true; IsMatchingFilteredTypes = true;
if (parent == null) if (parent == null)
IsRootViewModel = true; IsRootViewModel = true;
else else
PropertyDescription = DataModelPath?.GetPropertyDescription() ?? DataModel.DataModelDescription; PropertyDescription = DataModelPath?.GetPropertyDescription() ?? DataModel?.DataModelDescription;
} }
/// <summary> /// <summary>
@ -45,12 +45,12 @@ namespace Artemis.UI.Shared
/// <summary> /// <summary>
/// Gets the data model path to the property this view model is visualizing /// Gets the data model path to the property this view model is visualizing
/// </summary> /// </summary>
public DataModelPath DataModelPath { get; } public DataModelPath? DataModelPath { get; }
/// <summary> /// <summary>
/// Gets a string representation of the path backing this model /// Gets a string representation of the path backing this model
/// </summary> /// </summary>
public string Path => DataModelPath?.Path; public string? Path => DataModelPath?.Path;
/// <summary> /// <summary>
/// Gets the property depth of the view model /// Gets the property depth of the view model
@ -60,7 +60,7 @@ namespace Artemis.UI.Shared
/// <summary> /// <summary>
/// Gets the data model backing this view model /// Gets the data model backing this view model
/// </summary> /// </summary>
public DataModel DataModel public DataModel? DataModel
{ {
get => _dataModel; get => _dataModel;
protected set => SetAndNotify(ref _dataModel, value); protected set => SetAndNotify(ref _dataModel, value);
@ -69,7 +69,7 @@ namespace Artemis.UI.Shared
/// <summary> /// <summary>
/// Gets the property description of the property this view model is visualizing /// Gets the property description of the property this view model is visualizing
/// </summary> /// </summary>
public DataModelPropertyAttribute PropertyDescription public DataModelPropertyAttribute? PropertyDescription
{ {
get => _propertyDescription; get => _propertyDescription;
protected set => SetAndNotify(ref _propertyDescription, value); protected set => SetAndNotify(ref _propertyDescription, value);
@ -78,7 +78,7 @@ namespace Artemis.UI.Shared
/// <summary> /// <summary>
/// Gets the parent of this view model /// Gets the parent of this view model
/// </summary> /// </summary>
public DataModelVisualizationViewModel Parent public DataModelVisualizationViewModel? Parent
{ {
get => _parent; get => _parent;
protected set => SetAndNotify(ref _parent, value); protected set => SetAndNotify(ref _parent, value);
@ -119,7 +119,7 @@ namespace Artemis.UI.Shared
/// <summary> /// <summary>
/// Gets a user-friendly representation of the <see cref="DataModelPath" /> /// Gets a user-friendly representation of the <see cref="DataModelPath" />
/// </summary> /// </summary>
public virtual string DisplayPath => DataModelPath != null public virtual string? DisplayPath => DataModelPath != null
? string.Join(" ", DataModelPath.Segments.Select(s => s.GetPropertyDescription()?.Name ?? s.Identifier)) ? string.Join(" ", DataModelPath.Segments.Select(s => s.GetPropertyDescription()?.Name ?? s.Identifier))
: null; : null;
@ -128,18 +128,18 @@ namespace Artemis.UI.Shared
/// </summary> /// </summary>
/// <param name="dataModelUIService">The data model UI service used during update</param> /// <param name="dataModelUIService">The data model UI service used during update</param>
/// <param name="configuration">The configuration to apply while updating</param> /// <param name="configuration">The configuration to apply while updating</param>
public abstract void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration configuration); public abstract void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? configuration);
/// <summary> /// <summary>
/// Gets the current value of the property being visualized /// Gets the current value of the property being visualized
/// </summary> /// </summary>
/// <returns>The current value of the property being visualized</returns> /// <returns>The current value of the property being visualized</returns>
public virtual object GetCurrentValue() public virtual object? GetCurrentValue()
{ {
if (IsRootViewModel) if (IsRootViewModel)
return null; return null;
return DataModelPath.GetValue(); return DataModelPath?.GetValue();
} }
/// <summary> /// <summary>
@ -148,7 +148,7 @@ namespace Artemis.UI.Shared
/// </summary> /// </summary>
/// <param name="looseMatch">Whether the type may be a loose match, meaning it can be cast or converted</param> /// <param name="looseMatch">Whether the type may be a loose match, meaning it can be cast or converted</param>
/// <param name="filteredTypes">The types to filter</param> /// <param name="filteredTypes">The types to filter</param>
public void ApplyTypeFilter(bool looseMatch, params Type[] filteredTypes) public void ApplyTypeFilter(bool looseMatch, params Type[]? filteredTypes)
{ {
if (filteredTypes != null) if (filteredTypes != null)
{ {
@ -176,7 +176,7 @@ namespace Artemis.UI.Shared
} }
// If the type couldn't be retrieved either way, assume false // If the type couldn't be retrieved either way, assume false
Type type = DataModelPath?.GetPropertyType(); Type? type = DataModelPath?.GetPropertyType();
if (type == null) if (type == null)
{ {
IsMatchingFilteredTypes = false; IsMatchingFilteredTypes = false;
@ -197,12 +197,14 @@ namespace Artemis.UI.Shared
return 0; return 0;
} }
internal void PopulateProperties(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration dataModelUpdateConfiguration) internal void PopulateProperties(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? dataModelUpdateConfiguration)
{ {
if (IsRootViewModel && DataModel == null) if (IsRootViewModel && DataModel == null)
return; return;
Type modelType = IsRootViewModel ? DataModel.GetType() : DataModelPath.GetPropertyType(); Type? modelType = IsRootViewModel ? DataModel?.GetType() : DataModelPath?.GetPropertyType();
if (modelType == null)
throw new ArtemisSharedUIException("Failed to populate data model visualization properties, couldn't get a property type");
// Add missing static children // Add missing static children
foreach (PropertyInfo propertyInfo in modelType.GetProperties(BindingFlags.Public | BindingFlags.Instance).OrderBy(t => t.MetadataToken)) foreach (PropertyInfo propertyInfo in modelType.GetProperties(BindingFlags.Public | BindingFlags.Instance).OrderBy(t => t.MetadataToken))
@ -212,27 +214,30 @@ namespace Artemis.UI.Shared
continue; continue;
if (propertyInfo.GetCustomAttribute<DataModelIgnoreAttribute>() != null) if (propertyInfo.GetCustomAttribute<DataModelIgnoreAttribute>() != null)
continue; continue;
MethodInfo getMethod = propertyInfo.GetGetMethod(); MethodInfo? getMethod = propertyInfo.GetGetMethod();
if (getMethod == null || getMethod.GetParameters().Any()) if (getMethod == null || getMethod.GetParameters().Any())
continue; continue;
DataModelVisualizationViewModel child = CreateChild(dataModelUIService, childPath, GetChildDepth()); DataModelVisualizationViewModel? child = CreateChild(dataModelUIService, childPath, GetChildDepth());
if (child != null) if (child != null)
Children.Add(child); Children.Add(child);
} }
// Remove static children that should be hidden // Remove static children that should be hidden
ReadOnlyCollection<PropertyInfo> hiddenProperties = DataModel.GetHiddenProperties(); if (DataModel != null)
foreach (PropertyInfo hiddenProperty in hiddenProperties)
{ {
string childPath = AppendToPath(hiddenProperty.Name); ReadOnlyCollection<PropertyInfo> hiddenProperties = DataModel.GetHiddenProperties();
DataModelVisualizationViewModel toRemove = Children.FirstOrDefault(c => c.Path != null && c.Path == childPath); foreach (PropertyInfo hiddenProperty in hiddenProperties)
if (toRemove != null) {
Children.Remove(toRemove); string childPath = AppendToPath(hiddenProperty.Name);
DataModelVisualizationViewModel? toRemove = Children.FirstOrDefault(c => c.Path != null && c.Path == childPath);
if (toRemove != null)
Children.Remove(toRemove);
}
} }
// Add missing dynamic children // Add missing dynamic children
object value = Parent == null || Parent.IsRootViewModel ? DataModel : DataModelPath.GetValue(); object? value = Parent == null || Parent.IsRootViewModel ? DataModel : DataModelPath?.GetValue();
if (value is DataModel dataModel) if (value is DataModel dataModel)
foreach (KeyValuePair<string, DataModel> kvp in dataModel.DynamicDataModels) foreach (KeyValuePair<string, DataModel> kvp in dataModel.DynamicDataModels)
{ {
@ -240,19 +245,21 @@ namespace Artemis.UI.Shared
if (Children.Any(c => c.Path != null && c.Path.Equals(childPath))) if (Children.Any(c => c.Path != null && c.Path.Equals(childPath)))
continue; continue;
DataModelVisualizationViewModel child = CreateChild(dataModelUIService, childPath, GetChildDepth()); DataModelVisualizationViewModel? child = CreateChild(dataModelUIService, childPath, GetChildDepth());
if (child != null) if (child != null)
Children.Add(child); Children.Add(child);
} }
// Remove dynamic children that have been removed from the data model // Remove dynamic children that have been removed from the data model
List<DataModelVisualizationViewModel> toRemoveDynamic = Children.Where(c => !c.DataModelPath.IsValid).ToList(); List<DataModelVisualizationViewModel> toRemoveDynamic = Children.Where(c => c.DataModelPath != null && !c.DataModelPath.IsValid).ToList();
if (toRemoveDynamic.Any()) if (toRemoveDynamic.Any())
Children.RemoveRange(toRemoveDynamic); Children.RemoveRange(toRemoveDynamic);
} }
private DataModelVisualizationViewModel CreateChild(IDataModelUIService dataModelUIService, string path, int depth) private DataModelVisualizationViewModel? CreateChild(IDataModelUIService dataModelUIService, string path, int depth)
{ {
if (DataModel == null)
throw new ArtemisSharedUIException("Cannot create a data model visualization child VM for a parent without a data model");
if (depth > MaxDepth) if (depth > MaxDepth)
return null; return null;
@ -260,8 +267,8 @@ namespace Artemis.UI.Shared
if (!dataModelPath.IsValid) if (!dataModelPath.IsValid)
return null; return null;
PropertyInfo propertyInfo = dataModelPath.GetPropertyInfo(); PropertyInfo? propertyInfo = dataModelPath.GetPropertyInfo();
Type propertyType = dataModelPath.GetPropertyType(); Type? propertyType = dataModelPath.GetPropertyType();
// Skip properties decorated with DataModelIgnore // Skip properties decorated with DataModelIgnore
if (propertyInfo != null && Attribute.IsDefined(propertyInfo, typeof(DataModelIgnoreAttribute))) if (propertyInfo != null && Attribute.IsDefined(propertyInfo, typeof(DataModelIgnoreAttribute)))
@ -270,8 +277,11 @@ namespace Artemis.UI.Shared
if (DataModel.GetHiddenProperties().Any(p => p.Equals(propertyInfo))) if (DataModel.GetHiddenProperties().Any(p => p.Equals(propertyInfo)))
return null; return null;
if (propertyType == null)
return null;
// If a display VM was found, prefer to use that in any case // If a display VM was found, prefer to use that in any case
DataModelDisplayViewModel typeViewModel = dataModelUIService.GetDataModelDisplayViewModel(propertyType, PropertyDescription); DataModelDisplayViewModel? typeViewModel = dataModelUIService.GetDataModelDisplayViewModel(propertyType, PropertyDescription);
if (typeViewModel != null) if (typeViewModel != null)
return new DataModelPropertyViewModel(DataModel, this, dataModelPath) {DisplayViewModel = typeViewModel, Depth = depth}; return new DataModelPropertyViewModel(DataModel, this, dataModelPath) {DisplayViewModel = typeViewModel, Depth = depth};
// For primitives, create a property view model, it may be null that is fine // For primitives, create a property view model, it may be null that is fine

View File

@ -9,7 +9,7 @@ namespace Artemis.UI.Shared
/// </summary> /// </summary>
public class DataModelInputDynamicEventArgs : EventArgs public class DataModelInputDynamicEventArgs : EventArgs
{ {
internal DataModelInputDynamicEventArgs(DataModelPath dataModelPath) internal DataModelInputDynamicEventArgs(DataModelPath? dataModelPath)
{ {
DataModelPath = dataModelPath; DataModelPath = dataModelPath;
} }
@ -17,6 +17,6 @@ namespace Artemis.UI.Shared
/// <summary> /// <summary>
/// Gets the data model path that was selected /// Gets the data model path that was selected
/// </summary> /// </summary>
public DataModelPath DataModelPath { get; } public DataModelPath? DataModelPath { get; }
} }
} }

View File

@ -8,7 +8,7 @@ namespace Artemis.UI.Shared
/// </summary> /// </summary>
public class DataModelInputStaticEventArgs : EventArgs public class DataModelInputStaticEventArgs : EventArgs
{ {
internal DataModelInputStaticEventArgs(object value) internal DataModelInputStaticEventArgs(object? value)
{ {
Value = value; Value = value;
} }
@ -16,6 +16,6 @@ namespace Artemis.UI.Shared
/// <summary> /// <summary>
/// The value that was submitted /// The value that was submitted
/// </summary> /// </summary>
public object Value { get; } public object? Value { get; }
} }
} }

View File

@ -8,12 +8,12 @@ namespace Artemis.UI.Shared
/// </summary> /// </summary>
public class ProfileEventArgs : EventArgs public class ProfileEventArgs : EventArgs
{ {
internal ProfileEventArgs(Profile profile) internal ProfileEventArgs(Profile? profile)
{ {
Profile = profile; Profile = profile;
} }
internal ProfileEventArgs(Profile profile, Profile previousProfile) internal ProfileEventArgs(Profile? profile, Profile? previousProfile)
{ {
Profile = profile; Profile = profile;
PreviousProfile = previousProfile; PreviousProfile = previousProfile;
@ -22,7 +22,7 @@ namespace Artemis.UI.Shared
/// <summary> /// <summary>
/// Gets the profile the event was raised for /// Gets the profile the event was raised for
/// </summary> /// </summary>
public Profile Profile { get; } public Profile? Profile { get; }
/// <summary> /// <summary>
/// If applicable, the previous active profile before the event was raised /// If applicable, the previous active profile before the event was raised

View File

@ -8,12 +8,12 @@ namespace Artemis.UI.Shared
/// </summary> /// </summary>
public class RenderProfileElementEventArgs : EventArgs public class RenderProfileElementEventArgs : EventArgs
{ {
internal RenderProfileElementEventArgs(RenderProfileElement renderProfileElement) internal RenderProfileElementEventArgs(RenderProfileElement? renderProfileElement)
{ {
RenderProfileElement = renderProfileElement; RenderProfileElement = renderProfileElement;
} }
internal RenderProfileElementEventArgs(RenderProfileElement renderProfileElement, RenderProfileElement previousRenderProfileElement) internal RenderProfileElementEventArgs(RenderProfileElement? renderProfileElement, RenderProfileElement? previousRenderProfileElement)
{ {
RenderProfileElement = renderProfileElement; RenderProfileElement = renderProfileElement;
PreviousRenderProfileElement = previousRenderProfileElement; PreviousRenderProfileElement = previousRenderProfileElement;
@ -22,7 +22,7 @@ namespace Artemis.UI.Shared
/// <summary> /// <summary>
/// Gets the profile element the event was raised for /// Gets the profile element the event was raised for
/// </summary> /// </summary>
public RenderProfileElement RenderProfileElement { get; } public RenderProfileElement? RenderProfileElement { get; }
/// <summary> /// <summary>
/// If applicable, the previous active profile element before the event was raised /// If applicable, the previous active profile element before the event was raised

View File

@ -15,11 +15,6 @@ namespace Artemis.UI.Shared.LayerBrushes
/// </summary> /// </summary>
public abstract class LayerBrushConfigurationDialog : ILayerBrushConfigurationDialog public abstract class LayerBrushConfigurationDialog : ILayerBrushConfigurationDialog
{ {
/// <summary>
/// The layer brush this dialog belongs to
/// </summary>
internal BaseLayerBrush LayerBrush { get; set; }
/// <summary> /// <summary>
/// The type of view model the tab contains /// The type of view model the tab contains
/// </summary> /// </summary>

View File

@ -15,10 +15,11 @@ namespace Artemis.UI.Shared.LayerEffects
/// </summary> /// </summary>
public abstract class LayerEffectConfigurationDialog : ILayerEffectConfigurationDialog public abstract class LayerEffectConfigurationDialog : ILayerEffectConfigurationDialog
{ {
// TODO: See if this is still in use
/// <summary> /// <summary>
/// The layer effect this dialog belongs to /// The layer effect this dialog belongs to
/// </summary> /// </summary>
internal BaseLayerEffect LayerEffect { get; set; } public BaseLayerEffect? LayerEffect { get; set; }
/// <summary> /// <summary>
/// The type of view model the tab contains /// The type of view model the tab contains

View File

@ -15,11 +15,6 @@ namespace Artemis.UI.Shared
/// </summary> /// </summary>
public abstract class PluginConfigurationDialog : IPluginConfigurationDialog public abstract class PluginConfigurationDialog : IPluginConfigurationDialog
{ {
/// <summary>
/// The layer brush this dialog belongs to
/// </summary>
internal PluginFeature PluginFeature { get; set; }
/// <summary> /// <summary>
/// The type of view model the tab contains /// The type of view model the tab contains
/// </summary> /// </summary>

View File

@ -22,9 +22,10 @@ SOFTWARE. */
using System; using System;
// ReSharper disable InheritdocConsiderUsage #pragma warning disable 8618
#pragma warning disable 1591 #pragma warning disable 1591
// ReSharper disable InheritdocConsiderUsage
// ReSharper disable UnusedMember.Global // ReSharper disable UnusedMember.Global
// ReSharper disable MemberCanBePrivate.Global // ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedAutoPropertyAccessor.Global // ReSharper disable UnusedAutoPropertyAccessor.Global

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Stylet; using Stylet;
@ -12,7 +13,7 @@ namespace Artemis.UI.Shared
public abstract class PropertyInputViewModel<T> : PropertyInputViewModel public abstract class PropertyInputViewModel<T> : PropertyInputViewModel
{ {
private bool _inputDragging; private bool _inputDragging;
private T _inputValue; [AllowNull] private T _inputValue = default!;
/// <summary> /// <summary>
/// Creates a new instance of the <see cref="PropertyInputViewModel" /> class /// Creates a new instance of the <see cref="PropertyInputViewModel" /> class
@ -73,6 +74,7 @@ namespace Artemis.UI.Shared
/// <summary> /// <summary>
/// Gets or sets the input value /// Gets or sets the input value
/// </summary> /// </summary>
[AllowNull]
public T InputValue public T InputValue
{ {
get => _inputValue; get => _inputValue;

View File

@ -19,13 +19,13 @@ namespace Artemis.UI.Shared.Screens.Dialogs
public void Confirm() public void Confirm()
{ {
if (!Session.IsEnded) if (Session != null && !Session.IsEnded)
Session.Close(true); Session.Close(true);
} }
public void Cancel() public void Cancel()
{ {
if (!Session.IsEnded) if (Session != null && !Session.IsEnded)
Session.Close(false); Session.Close(false);
} }
} }

View File

@ -9,12 +9,12 @@ namespace Artemis.UI.Shared.Screens.Exceptions
{ {
private List<DialogException> _exceptions; private List<DialogException> _exceptions;
public ExceptionViewModel(string message, Exception exception) public ExceptionViewModel(string message, Exception? exception)
{ {
Header = message; Header = message;
Exceptions = new List<DialogException>(); _exceptions = new List<DialogException>();
Exception currentException = exception; Exception? currentException = exception;
while (currentException != null) while (currentException != null)
{ {
Exceptions.Add(new DialogException(currentException)); Exceptions.Add(new DialogException(currentException));

View File

@ -59,7 +59,7 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
set => SetAndNotify(ref _willRemoveColorStop, value); set => SetAndNotify(ref _willRemoveColorStop, value);
} }
private void ColorStopOnPropertyChanged(object sender, PropertyChangedEventArgs e) private void ColorStopOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
{ {
_gradientEditorViewModel.ColorGradient.OnColorValuesUpdated(); _gradientEditorViewModel.ColorGradient.OnColorValuesUpdated();
} }
@ -90,7 +90,7 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
if (!((IInputElement) sender).IsMouseCaptured) if (!((IInputElement) sender).IsMouseCaptured)
return; return;
Canvas parent = VisualTreeUtilities.FindParent<Canvas>((DependencyObject) sender, null); Canvas? parent = VisualTreeUtilities.FindParent<Canvas>((DependencyObject) sender, null);
Point position = e.GetPosition(parent); Point position = e.GetPosition(parent);
if (position.Y > 50) if (position.Y > 50)
{ {
@ -103,8 +103,8 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
double minValue = 0.0; double minValue = 0.0;
double maxValue = _gradientEditorViewModel.PreviewWidth; double maxValue = _gradientEditorViewModel.PreviewWidth;
List<ColorGradientStop> stops = _gradientEditorViewModel.ColorGradient.Stops.OrderBy(s => s.Position).ToList(); List<ColorGradientStop> stops = _gradientEditorViewModel.ColorGradient.Stops.OrderBy(s => s.Position).ToList();
ColorGradientStop previous = stops.IndexOf(ColorStop) >= 1 ? stops[stops.IndexOf(ColorStop) - 1] : null; ColorGradientStop? previous = stops.IndexOf(ColorStop) >= 1 ? stops[stops.IndexOf(ColorStop) - 1] : null;
ColorGradientStop next = stops.IndexOf(ColorStop) + 1 < stops.Count ? stops[stops.IndexOf(ColorStop) + 1] : null; ColorGradientStop? next = stops.IndexOf(ColorStop) + 1 < stops.Count ? stops[stops.IndexOf(ColorStop) + 1] : null;
if (previous != null) if (previous != null)
minValue = previous.Position * _gradientEditorViewModel.PreviewWidth; minValue = previous.Position * _gradientEditorViewModel.PreviewWidth;
if (next != null) if (next != null)

View File

@ -14,7 +14,7 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
{ {
private readonly List<ColorGradientStop> _originalStops; private readonly List<ColorGradientStop> _originalStops;
private double _previewWidth; private double _previewWidth;
private ColorStopViewModel _selectedColorStopViewModel; private ColorStopViewModel? _selectedColorStopViewModel;
public GradientEditorViewModel(ColorGradient colorGradient) public GradientEditorViewModel(ColorGradient colorGradient)
{ {
@ -28,7 +28,7 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
public BindableCollection<ColorStopViewModel> ColorStopViewModels { get; set; } public BindableCollection<ColorStopViewModel> ColorStopViewModels { get; set; }
public ColorStopViewModel SelectedColorStopViewModel public ColorStopViewModel? SelectedColorStopViewModel
{ {
get => _selectedColorStopViewModel; get => _selectedColorStopViewModel;
set set
@ -50,7 +50,7 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
public void AddColorStop(object sender, MouseEventArgs e) public void AddColorStop(object sender, MouseEventArgs e)
{ {
Canvas child = VisualTreeUtilities.FindChild<Canvas>((DependencyObject) sender, null); Canvas? child = VisualTreeUtilities.FindChild<Canvas>((DependencyObject) sender, null);
float position = (float) (e.GetPosition(child).X / PreviewWidth); float position = (float) (e.GetPosition(child).X / PreviewWidth);
ColorGradientStop stop = new ColorGradientStop(ColorGradient.GetColor(position), position); ColorGradientStop stop = new ColorGradientStop(ColorGradient.GetColor(position), position);
ColorGradient.Stops.Add(stop); ColorGradient.Stops.Add(stop);
@ -74,11 +74,11 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
public Point GetPositionInPreview(object sender, MouseEventArgs e) public Point GetPositionInPreview(object sender, MouseEventArgs e)
{ {
Canvas parent = VisualTreeUtilities.FindParent<Canvas>((DependencyObject) sender, null); Canvas? parent = VisualTreeUtilities.FindParent<Canvas>((DependencyObject) sender, null);
return e.GetPosition(parent); return e.GetPosition(parent);
} }
public void SelectColorStop(ColorStopViewModel colorStopViewModel) public void SelectColorStop(ColorStopViewModel? colorStopViewModel)
{ {
SelectedColorStopViewModel = colorStopViewModel; SelectedColorStopViewModel = colorStopViewModel;
foreach (ColorStopViewModel stopViewModel in ColorStopViewModels) foreach (ColorStopViewModel stopViewModel in ColorStopViewModels)
@ -87,7 +87,7 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
public void Confirm() public void Confirm()
{ {
if (!Session.IsEnded) if (Session != null && !Session.IsEnded)
Session.Close(true); Session.Close(true);
} }
@ -98,11 +98,11 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
ColorGradient.Stops.AddRange(_originalStops); ColorGradient.Stops.AddRange(_originalStops);
ColorGradient.OnColorValuesUpdated(); ColorGradient.OnColorValuesUpdated();
if (!Session.IsEnded) if (Session != null && !Session.IsEnded)
Session.Close(false); Session.Close(false);
} }
private void UpdateColorStopViewModels(object sender, PropertyChangedEventArgs e) private void UpdateColorStopViewModels(object? sender, PropertyChangedEventArgs e)
{ {
if (e.PropertyName != nameof(PreviewWidth)) return; if (e.PropertyName != nameof(PreviewWidth)) return;
foreach (ColorGradientStop colorStop in ColorGradient.Stops.OrderBy(s => s.Position)) foreach (ColorGradientStop colorStop in ColorGradient.Stops.OrderBy(s => s.Position))

View File

@ -56,7 +56,7 @@ namespace Artemis.UI.Shared.Services
_overlayColor = new SKColor(color.R, color.G, color.B, color.A); _overlayColor = new SKColor(color.R, color.G, color.B, color.A);
} }
private void RenderColorPickerOverlay(object sender, FrameRenderingEventArgs e) private void RenderColorPickerOverlay(object? sender, FrameRenderingEventArgs e)
{ {
if (_mustRenderOverlay) if (_mustRenderOverlay)
_overlayOpacity += 0.2f; _overlayOpacity += 0.2f;

View File

@ -46,7 +46,9 @@ namespace Artemis.UI.Shared.Services
public void UpdateModules(DataModelPropertiesViewModel mainDataModelVisualization) public void UpdateModules(DataModelPropertiesViewModel mainDataModelVisualization)
{ {
List<DataModelVisualizationViewModel> disabledChildren = mainDataModelVisualization.Children.Where(d => !d.DataModel.Feature.IsEnabled).ToList(); List<DataModelVisualizationViewModel> disabledChildren = mainDataModelVisualization.Children
.Where(d => d.DataModel != null && !d.DataModel.Feature.IsEnabled)
.ToList();
foreach (DataModelVisualizationViewModel child in disabledChildren) foreach (DataModelVisualizationViewModel child in disabledChildren)
mainDataModelVisualization.Children.Remove(child); mainDataModelVisualization.Children.Remove(child);
@ -63,24 +65,24 @@ namespace Artemis.UI.Shared.Services
mainDataModelVisualization.Update(this, null); mainDataModelVisualization.Update(this, null);
} }
public DataModelPropertiesViewModel GetPluginDataModelVisualization(PluginFeature pluginFeature, bool includeMainDataModel) public DataModelPropertiesViewModel? GetPluginDataModelVisualization(PluginFeature pluginFeature, bool includeMainDataModel)
{ {
if (includeMainDataModel) if (includeMainDataModel)
{ {
DataModelPropertiesViewModel mainDataModel = GetMainDataModelVisualization(); DataModelPropertiesViewModel mainDataModel = GetMainDataModelVisualization();
// If the main data model already includes the plugin data model we're done // If the main data model already includes the plugin data model we're done
if (mainDataModel.Children.Any(c => c.DataModel.Feature == pluginFeature)) if (mainDataModel.Children.Any(c => c.DataModel?.Feature == pluginFeature))
return mainDataModel; return mainDataModel;
// Otherwise get just the plugin data model and add it // Otherwise get just the plugin data model and add it
DataModelPropertiesViewModel pluginDataModel = GetPluginDataModelVisualization(pluginFeature, false); DataModelPropertiesViewModel? pluginDataModel = GetPluginDataModelVisualization(pluginFeature, false);
if (pluginDataModel != null) if (pluginDataModel != null)
mainDataModel.Children.Add(pluginDataModel); mainDataModel.Children.Add(pluginDataModel);
return mainDataModel; return mainDataModel;
} }
DataModel dataModel = _dataModelService.GetPluginDataModel(pluginFeature); DataModel? dataModel = _dataModelService.GetPluginDataModel(pluginFeature);
if (dataModel == null) if (dataModel == null)
return null; return null;
@ -93,15 +95,14 @@ namespace Artemis.UI.Shared.Services
return viewModel; return viewModel;
} }
public DataModelVisualizationRegistration RegisterDataModelInput<T>(Plugin plugin, IReadOnlyCollection<Type> compatibleConversionTypes = null) where T : DataModelInputViewModel public DataModelVisualizationRegistration RegisterDataModelInput<T>(Plugin plugin, IReadOnlyCollection<Type>? compatibleConversionTypes = null) where T : DataModelInputViewModel
{ {
if (compatibleConversionTypes == null) compatibleConversionTypes ??= new List<Type>();
compatibleConversionTypes = new List<Type>();
Type viewModelType = typeof(T); Type viewModelType = typeof(T);
lock (_registeredDataModelEditors) lock (_registeredDataModelEditors)
{ {
Type supportedType = viewModelType.BaseType.GetGenericArguments()[0]; Type supportedType = viewModelType.BaseType!.GetGenericArguments()[0];
DataModelVisualizationRegistration existing = _registeredDataModelEditors.FirstOrDefault(r => r.SupportedType == supportedType); DataModelVisualizationRegistration? existing = _registeredDataModelEditors.FirstOrDefault(r => r.SupportedType == supportedType);
if (existing != null) if (existing != null)
{ {
if (existing.Plugin != plugin) if (existing.Plugin != plugin)
@ -129,8 +130,8 @@ namespace Artemis.UI.Shared.Services
Type viewModelType = typeof(T); Type viewModelType = typeof(T);
lock (_registeredDataModelDisplays) lock (_registeredDataModelDisplays)
{ {
Type supportedType = viewModelType.BaseType.GetGenericArguments()[0]; Type supportedType = viewModelType.BaseType!.GetGenericArguments()[0];
DataModelVisualizationRegistration existing = _registeredDataModelDisplays.FirstOrDefault(r => r.SupportedType == supportedType); DataModelVisualizationRegistration? existing = _registeredDataModelDisplays.FirstOrDefault(r => r.SupportedType == supportedType);
if (existing != null) if (existing != null)
{ {
if (existing.Plugin != plugin) if (existing.Plugin != plugin)
@ -174,15 +175,21 @@ namespace Artemis.UI.Shared.Services
} }
} }
public DataModelDisplayViewModel GetDataModelDisplayViewModel(Type propertyType, DataModelPropertyAttribute description, bool fallBackToDefault) public DataModelDisplayViewModel? GetDataModelDisplayViewModel(Type propertyType, DataModelPropertyAttribute? description, bool fallBackToDefault)
{ {
lock (_registeredDataModelDisplays) lock (_registeredDataModelDisplays)
{ {
DataModelDisplayViewModel result; DataModelDisplayViewModel? result;
DataModelVisualizationRegistration match = _registeredDataModelDisplays.FirstOrDefault(d => d.SupportedType == propertyType); DataModelVisualizationRegistration? match = _registeredDataModelDisplays.FirstOrDefault(d => d.SupportedType == propertyType);
if (match != null) if (match != null)
{
// If this ever happens something is likely wrong with the plugin unload detection
if (match.Plugin.Kernel == null)
throw new ArtemisSharedUIException("Cannot GetDataModelDisplayViewModel for a registration by an uninitialized plugin");
result = (DataModelDisplayViewModel) match.Plugin.Kernel.Get(match.ViewModelType); result = (DataModelDisplayViewModel) match.Plugin.Kernel.Get(match.ViewModelType);
}
else if (!fallBackToDefault) else if (!fallBackToDefault)
result = null; result = null;
else else
@ -195,15 +202,15 @@ namespace Artemis.UI.Shared.Services
} }
} }
public DataModelInputViewModel GetDataModelInputViewModel(Type propertyType, DataModelPropertyAttribute description, object initialValue, Action<object, bool> updateCallback) public DataModelInputViewModel? GetDataModelInputViewModel(Type propertyType, DataModelPropertyAttribute? description, object? initialValue, Action<object?, bool> updateCallback)
{ {
lock (_registeredDataModelEditors) lock (_registeredDataModelEditors)
{ {
// Prefer a VM that natively supports the type // Prefer a VM that natively supports the type
DataModelVisualizationRegistration match = _registeredDataModelEditors.FirstOrDefault(d => d.SupportedType == propertyType); DataModelVisualizationRegistration? match = _registeredDataModelEditors.FirstOrDefault(d => d.SupportedType == propertyType);
// Fall back on a VM that supports the type through conversion // Fall back on a VM that supports the type through conversion
if (match == null) if (match == null)
match = _registeredDataModelEditors.FirstOrDefault(d => d.CompatibleConversionTypes.Contains(propertyType)); match = _registeredDataModelEditors.FirstOrDefault(d => d.CompatibleConversionTypes != null && d.CompatibleConversionTypes.Contains(propertyType));
// Lastly try getting an enum VM if the provided type is an enum // Lastly try getting an enum VM if the provided type is an enum
if (match == null && propertyType.IsEnum) if (match == null && propertyType.IsEnum)
match = _registeredDataModelEditors.FirstOrDefault(d => d.SupportedType == typeof(Enum)); match = _registeredDataModelEditors.FirstOrDefault(d => d.SupportedType == typeof(Enum));
@ -229,7 +236,7 @@ namespace Artemis.UI.Shared.Services
return _dataModelVmFactory.DataModelStaticViewModel(targetType, targetDescription); return _dataModelVmFactory.DataModelStaticViewModel(targetType, targetDescription);
} }
private DataModelInputViewModel InstantiateDataModelInputViewModel(DataModelVisualizationRegistration registration, DataModelPropertyAttribute description, object initialValue) private DataModelInputViewModel InstantiateDataModelInputViewModel(DataModelVisualizationRegistration registration, DataModelPropertyAttribute? description, object? initialValue)
{ {
// This assumes the type can be converted, that has been checked when the VM was created // This assumes the type can be converted, that has been checked when the VM was created
if (initialValue != null && initialValue.GetType() != registration.SupportedType) if (initialValue != null && initialValue.GetType() != registration.SupportedType)
@ -240,6 +247,11 @@ namespace Artemis.UI.Shared.Services
new ConstructorArgument("targetDescription", description), new ConstructorArgument("targetDescription", description),
new ConstructorArgument("initialValue", initialValue) new ConstructorArgument("initialValue", initialValue)
}; };
// If this ever happens something is likely wrong with the plugin unload detection
if (registration.Plugin.Kernel == null)
throw new ArtemisSharedUIException("Cannot InstantiateDataModelInputViewModel for a registration by an uninitialized plugin");
DataModelInputViewModel viewModel = (DataModelInputViewModel) registration.Plugin.Kernel.Get(registration.ViewModelType, parameters); DataModelInputViewModel viewModel = (DataModelInputViewModel) registration.Plugin.Kernel.Get(registration.ViewModelType, parameters);
viewModel.CompatibleConversionTypes = registration.CompatibleConversionTypes; viewModel.CompatibleConversionTypes = registration.CompatibleConversionTypes;
return viewModel; return viewModel;

View File

@ -1,8 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using Artemis.Core; using Artemis.Core;
@ -16,13 +14,12 @@ using Stylet;
namespace Artemis.UI.Shared.Services namespace Artemis.UI.Shared.Services
{ {
// TODO: Become plugin-aware and use plugin kernel if injected into a plugin
internal class DialogService : IDialogService internal class DialogService : IDialogService
{ {
private readonly IKernel _kernel; private readonly IKernel _kernel;
private readonly IPluginManagementService _pluginManagementService;
private readonly IViewManager _viewManager; private readonly IViewManager _viewManager;
private readonly IWindowManager _windowManager; private readonly IWindowManager _windowManager;
private readonly IPluginManagementService _pluginManagementService;
public DialogService(IKernel kernel, IViewManager viewManager, IWindowManager windowManager, IPluginManagementService pluginManagementService) public DialogService(IKernel kernel, IViewManager viewManager, IWindowManager windowManager, IPluginManagementService pluginManagementService)
{ {
@ -35,12 +32,12 @@ namespace Artemis.UI.Shared.Services
private async Task<object> ShowDialog<T>(IParameter[] parameters) where T : DialogViewModelBase private async Task<object> ShowDialog<T>(IParameter[] parameters) where T : DialogViewModelBase
{ {
if (parameters == null) throw new ArgumentNullException(nameof(parameters)); if (parameters == null) throw new ArgumentNullException(nameof(parameters));
return await ShowDialog("RootDialog", _kernel.Get<T>(parameters)); return await ShowDialog("RootDialog", GetBestKernel().Get<T>(parameters));
} }
private async Task<object> ShowDialog(string identifier, DialogViewModelBase viewModel) private async Task<object> ShowDialog(string? identifier, DialogViewModelBase viewModel)
{ {
Task<object> result = null; Task<object>? result = null;
await Execute.OnUIThreadAsync(() => await Execute.OnUIThreadAsync(() =>
{ {
UIElement view = _viewManager.CreateViewForModel(viewModel); UIElement view = _viewManager.CreateViewForModel(viewModel);
@ -52,12 +49,22 @@ namespace Artemis.UI.Shared.Services
result = DialogHost.Show(view, identifier, viewModel.OnDialogOpened, viewModel.OnDialogClosed); result = DialogHost.Show(view, identifier, viewModel.OnDialogOpened, viewModel.OnDialogClosed);
}); });
if (result == null)
throw new ArtemisSharedUIException("Failed to show dialog host");
return await result; return await result;
} }
private async Task<object> ShowDialogAt<T>(string identifier, IParameter[] parameters) where T : DialogViewModelBase
{
if (parameters == null)
throw new ArgumentNullException(nameof(parameters));
return await ShowDialog(identifier, GetBestKernel().Get<T>(parameters));
}
public async Task<bool> ShowConfirmDialog(string header, string text, string confirmText = "Confirm", string cancelText = "Cancel") public async Task<bool> ShowConfirmDialog(string header, string text, string confirmText = "Confirm", string cancelText = "Cancel")
{ {
IParameter[] arguments = { IParameter[] arguments =
{
new ConstructorArgument("header", header), new ConstructorArgument("header", header),
new ConstructorArgument("text", text), new ConstructorArgument("text", text),
new ConstructorArgument("confirmText", confirmText.ToUpper()), new ConstructorArgument("confirmText", confirmText.ToUpper()),
@ -69,7 +76,9 @@ namespace Artemis.UI.Shared.Services
public async Task<bool> ShowConfirmDialogAt(string identifier, string header, string text, string confirmText = "Confirm", string cancelText = "Cancel") public async Task<bool> ShowConfirmDialogAt(string identifier, string header, string text, string confirmText = "Confirm", string cancelText = "Cancel")
{ {
IParameter[] arguments = { if (identifier == null) throw new ArgumentNullException(nameof(identifier));
IParameter[] arguments =
{
new ConstructorArgument("header", header), new ConstructorArgument("header", header),
new ConstructorArgument("text", text), new ConstructorArgument("text", text),
new ConstructorArgument("confirmText", confirmText.ToUpper()), new ConstructorArgument("confirmText", confirmText.ToUpper()),
@ -81,7 +90,7 @@ namespace Artemis.UI.Shared.Services
public async Task<object> ShowDialog<T>() where T : DialogViewModelBase public async Task<object> ShowDialog<T>() where T : DialogViewModelBase
{ {
return await ShowDialog("RootDialog", _kernel.Get<T>()); return await ShowDialog("RootDialog", GetBestKernel().Get<T>());
} }
public Task<object> ShowDialog<T>(Dictionary<string, object> parameters) where T : DialogViewModelBase public Task<object> ShowDialog<T>(Dictionary<string, object> parameters) where T : DialogViewModelBase
@ -95,32 +104,28 @@ namespace Artemis.UI.Shared.Services
public async Task<object> ShowDialogAt<T>(string identifier) where T : DialogViewModelBase public async Task<object> ShowDialogAt<T>(string identifier) where T : DialogViewModelBase
{ {
return await ShowDialog(identifier, _kernel.Get<T>()); if (identifier == null) throw new ArgumentNullException(nameof(identifier));
return await ShowDialog(identifier, GetBestKernel().Get<T>());
} }
public async Task<object> ShowDialogAt<T>(string identifier, Dictionary<string, object> parameters) where T : DialogViewModelBase public async Task<object> ShowDialogAt<T>(string identifier, Dictionary<string, object> parameters) where T : DialogViewModelBase
{ {
if (parameters == null) if (identifier == null) throw new ArgumentNullException(nameof(identifier));
throw new ArgumentNullException(nameof(parameters)); if (parameters == null) throw new ArgumentNullException(nameof(parameters));
IParameter[] paramsArray = parameters.Select(kv => new ConstructorArgument(kv.Key, kv.Value)).Cast<IParameter>().ToArray(); IParameter[] paramsArray = parameters.Select(kv => new ConstructorArgument(kv.Key, kv.Value)).Cast<IParameter>().ToArray();
return await ShowDialogAt<T>(identifier, paramsArray); return await ShowDialogAt<T>(identifier, paramsArray);
} }
private async Task<object> ShowDialogAt<T>(string identifier, IParameter[] parameters) where T : DialogViewModelBase
{
Plugin callingPlugin = _pluginManagementService.GetCallingPlugin();
if (parameters == null)
throw new ArgumentNullException(nameof(parameters));
if (callingPlugin != null)
return await ShowDialog(identifier, callingPlugin.Kernel.Get<T>(parameters));
return await ShowDialog(identifier, _kernel.Get<T>(parameters));
}
public void ShowExceptionDialog(string message, Exception exception) public void ShowExceptionDialog(string message, Exception exception)
{ {
_windowManager.ShowDialog(new ExceptionViewModel(message, exception)); _windowManager.ShowDialog(new ExceptionViewModel(message, exception));
} }
private IKernel GetBestKernel()
{
Plugin? callingPlugin = _pluginManagementService.GetCallingPlugin();
return callingPlugin?.Kernel ?? _kernel;
}
} }
} }

View File

@ -13,7 +13,7 @@ namespace Artemis.UI.Shared.Services
_viewManager = viewManager; _viewManager = viewManager;
} }
public DialogViewModelBase ActiveDialogViewModel { get; set; } public DialogViewModelBase? ActiveDialogViewModel { get; set; }
public bool IsOpen { get; set; } public bool IsOpen { get; set; }
public void OpenDialog(DialogViewModelBase viewModel, string dialogIdentifier) public void OpenDialog(DialogViewModelBase viewModel, string dialogIdentifier)

View File

@ -37,7 +37,7 @@ namespace Artemis.UI.Shared.Services
/// <param name="pluginFeature">The plugin feature to create hte data model visualization view model for</param> /// <param name="pluginFeature">The plugin feature to create hte data model visualization view model for</param>
/// <param name="includeMainDataModel">Whether or not also to include the main data model</param> /// <param name="includeMainDataModel">Whether or not also to include the main data model</param>
/// <returns>A data model visualization view model containing the data model of the provided feature</returns> /// <returns>A data model visualization view model containing the data model of the provided feature</returns>
DataModelPropertiesViewModel GetPluginDataModelVisualization(PluginFeature pluginFeature, bool includeMainDataModel); DataModelPropertiesViewModel? GetPluginDataModelVisualization(PluginFeature pluginFeature, bool includeMainDataModel);
/// <summary> /// <summary>
/// Updates the children of the provided main data model visualization, removing disabled children and adding newly /// Updates the children of the provided main data model visualization, removing disabled children and adding newly
@ -89,7 +89,7 @@ namespace Artemis.UI.Shared.Services
/// returned if nothing else is found /// returned if nothing else is found
/// </param> /// </param>
/// <returns>The most appropriate display view model for the provided <paramref name="propertyType"></paramref></returns> /// <returns>The most appropriate display view model for the provided <paramref name="propertyType"></paramref></returns>
DataModelDisplayViewModel GetDataModelDisplayViewModel(Type propertyType, DataModelPropertyAttribute description, bool fallBackToDefault = false); DataModelDisplayViewModel? GetDataModelDisplayViewModel(Type propertyType, DataModelPropertyAttribute? description, bool fallBackToDefault = false);
/// <summary> /// <summary>
/// Creates the most appropriate input view model for the provided <paramref name="propertyType" /> that allows /// Creates the most appropriate input view model for the provided <paramref name="propertyType" /> that allows
@ -100,7 +100,7 @@ namespace Artemis.UI.Shared.Services
/// <param name="initialValue">The initial value to show in the input</param> /// <param name="initialValue">The initial value to show in the input</param>
/// <param name="updateCallback">A function to call whenever the input was updated (submitted or not)</param> /// <param name="updateCallback">A function to call whenever the input was updated (submitted or not)</param>
/// <returns>The most appropriate input view model for the provided <paramref name="propertyType" /></returns> /// <returns>The most appropriate input view model for the provided <paramref name="propertyType" /></returns>
DataModelInputViewModel GetDataModelInputViewModel(Type propertyType, DataModelPropertyAttribute description, object initialValue, Action<object, bool> updateCallback); DataModelInputViewModel? GetDataModelInputViewModel(Type propertyType, DataModelPropertyAttribute? description, object? initialValue, Action<object?, bool> updateCallback);
/// <summary> /// <summary>
/// Creates a view model that allows selecting a value from the data model /// Creates a view model that allows selecting a value from the data model

View File

@ -14,17 +14,17 @@ namespace Artemis.UI.Shared.Services
/// <summary> /// <summary>
/// Gets the currently selected profile /// Gets the currently selected profile
/// </summary> /// </summary>
Profile SelectedProfile { get; } Profile? SelectedProfile { get; }
/// <summary> /// <summary>
/// Gets the currently selected profile element /// Gets the currently selected profile element
/// </summary> /// </summary>
RenderProfileElement SelectedProfileElement { get; } RenderProfileElement? SelectedProfileElement { get; }
/// <summary> /// <summary>
/// Gets the currently selected data binding property /// Gets the currently selected data binding property
/// </summary> /// </summary>
ILayerProperty SelectedDataBinding { get; } ILayerProperty? SelectedDataBinding { get; }
/// <summary> /// <summary>
/// Gets or sets the current time /// Gets or sets the current time
@ -45,7 +45,7 @@ namespace Artemis.UI.Shared.Services
/// Changes the selected profile /// Changes the selected profile
/// </summary> /// </summary>
/// <param name="profile">The profile to select</param> /// <param name="profile">The profile to select</param>
void ChangeSelectedProfile(Profile profile); void ChangeSelectedProfile(Profile? profile);
/// <summary> /// <summary>
/// Updates the selected profile and saves it to persistent storage /// Updates the selected profile and saves it to persistent storage
@ -56,7 +56,7 @@ namespace Artemis.UI.Shared.Services
/// Changes the selected profile element /// Changes the selected profile element
/// </summary> /// </summary>
/// <param name="profileElement">The profile element to select</param> /// <param name="profileElement">The profile element to select</param>
void ChangeSelectedProfileElement(RenderProfileElement profileElement); void ChangeSelectedProfileElement(RenderProfileElement? profileElement);
/// <summary> /// <summary>
/// Updates the selected profile element and saves the profile it is contained in to persistent storage /// Updates the selected profile element and saves the profile it is contained in to persistent storage
@ -67,7 +67,7 @@ namespace Artemis.UI.Shared.Services
/// Changes the selected data binding property /// Changes the selected data binding property
/// </summary> /// </summary>
/// <param name="layerProperty">The data binding property to select</param> /// <param name="layerProperty">The data binding property to select</param>
void ChangeSelectedDataBinding(ILayerProperty layerProperty); void ChangeSelectedDataBinding(ILayerProperty? layerProperty);
/// <summary> /// <summary>
/// Updates the profile preview, forcing UI-elements to re-render /// Updates the profile preview, forcing UI-elements to re-render
@ -90,7 +90,7 @@ namespace Artemis.UI.Shared.Services
/// Gets the current module the profile editor is initialized for /// Gets the current module the profile editor is initialized for
/// </summary> /// </summary>
/// <returns>The current module the profile editor is initialized for</returns> /// <returns>The current module the profile editor is initialized for</returns>
ProfileModule GetCurrentModule(); ProfileModule? GetCurrentModule();
/// <summary> /// <summary>
/// Occurs when a new profile is selected /// Occurs when a new profile is selected
@ -173,6 +173,6 @@ namespace Artemis.UI.Shared.Services
/// If a matching registration is found, creates a new <see cref="PropertyInputViewModel{T}" /> supporting /// If a matching registration is found, creates a new <see cref="PropertyInputViewModel{T}" /> supporting
/// <typeparamref name="T" /> /// <typeparamref name="T" />
/// </summary> /// </summary>
PropertyInputViewModel<T> CreatePropertyInputViewModel<T>(LayerProperty<T> layerProperty); PropertyInputViewModel<T>? CreatePropertyInputViewModel<T>(LayerProperty<T> layerProperty);
} }
} }

View File

@ -22,7 +22,7 @@ namespace Artemis.UI.Shared.Services
private readonly object _selectedProfileLock = new object(); private readonly object _selectedProfileLock = new object();
private TimeSpan _currentTime; private TimeSpan _currentTime;
private int _pixelsPerSecond; private int _pixelsPerSecond;
private IKernel _kernel; private readonly IKernel _kernel;
public ProfileEditorService(IProfileService profileService, IKernel kernel, ILogger logger, ICoreService coreService) public ProfileEditorService(IProfileService profileService, IKernel kernel, ILogger logger, ICoreService coreService)
{ {
@ -42,9 +42,9 @@ namespace Artemis.UI.Shared.Services
} }
public ReadOnlyCollection<PropertyInputRegistration> RegisteredPropertyEditors => _registeredPropertyEditors.AsReadOnly(); public ReadOnlyCollection<PropertyInputRegistration> RegisteredPropertyEditors => _registeredPropertyEditors.AsReadOnly();
public Profile SelectedProfile { get; private set; } public Profile? SelectedProfile { get; private set; }
public RenderProfileElement SelectedProfileElement { get; private set; } public RenderProfileElement? SelectedProfileElement { get; private set; }
public ILayerProperty SelectedDataBinding { get; private set; } public ILayerProperty? SelectedDataBinding { get; private set; }
public TimeSpan CurrentTime public TimeSpan CurrentTime
{ {
@ -71,7 +71,7 @@ namespace Artemis.UI.Shared.Services
} }
} }
public void ChangeSelectedProfile(Profile profile) public void ChangeSelectedProfile(Profile? profile)
{ {
lock (_selectedProfileLock) lock (_selectedProfileLock)
{ {
@ -103,14 +103,16 @@ namespace Artemis.UI.Shared.Services
lock (_selectedProfileLock) lock (_selectedProfileLock)
{ {
_logger.Verbose("UpdateSelectedProfile {profile}", SelectedProfile); _logger.Verbose("UpdateSelectedProfile {profile}", SelectedProfile);
_profileService.UpdateProfile(SelectedProfile, true); if (SelectedProfile == null)
return;
_profileService.UpdateProfile(SelectedProfile, true);
OnSelectedProfileChanged(new ProfileEventArgs(SelectedProfile)); OnSelectedProfileChanged(new ProfileEventArgs(SelectedProfile));
UpdateProfilePreview(); UpdateProfilePreview();
} }
} }
public void ChangeSelectedProfileElement(RenderProfileElement profileElement) public void ChangeSelectedProfileElement(RenderProfileElement? profileElement)
{ {
lock (_selectedProfileElementLock) lock (_selectedProfileElementLock)
{ {
@ -131,13 +133,16 @@ namespace Artemis.UI.Shared.Services
lock (_selectedProfileElementLock) lock (_selectedProfileElementLock)
{ {
_logger.Verbose("UpdateSelectedProfileElement {profile}", SelectedProfileElement); _logger.Verbose("UpdateSelectedProfileElement {profile}", SelectedProfileElement);
if (SelectedProfile == null)
return;
_profileService.UpdateProfile(SelectedProfile, true); _profileService.UpdateProfile(SelectedProfile, true);
UpdateProfilePreview();
OnSelectedProfileElementUpdated(new RenderProfileElementEventArgs(SelectedProfileElement)); OnSelectedProfileElementUpdated(new RenderProfileElementEventArgs(SelectedProfileElement));
UpdateProfilePreview();
} }
} }
public void ChangeSelectedDataBinding(ILayerProperty layerProperty) public void ChangeSelectedDataBinding(ILayerProperty? layerProperty)
{ {
SelectedDataBinding = layerProperty; SelectedDataBinding = layerProperty;
OnSelectedDataBindingChanged(); OnSelectedDataBindingChanged();
@ -159,6 +164,9 @@ namespace Artemis.UI.Shared.Services
public bool UndoUpdateProfile() public bool UndoUpdateProfile()
{ {
if (SelectedProfile == null)
return false;
bool undid = _profileService.UndoUpdateProfile(SelectedProfile); bool undid = _profileService.UndoUpdateProfile(SelectedProfile);
if (!undid) if (!undid)
return false; return false;
@ -169,6 +177,9 @@ namespace Artemis.UI.Shared.Services
public bool RedoUpdateProfile() public bool RedoUpdateProfile()
{ {
if (SelectedProfile == null)
return false;
bool redid = _profileService.RedoUpdateProfile(SelectedProfile); bool redid = _profileService.RedoUpdateProfile(SelectedProfile);
if (!redid) if (!redid)
return false; return false;
@ -189,7 +200,8 @@ namespace Artemis.UI.Shared.Services
lock (_registeredPropertyEditors) lock (_registeredPropertyEditors)
{ {
Type supportedType = viewModelType.BaseType.GetGenericArguments()[0]; // Indirectly checked if there's a BaseType above
Type supportedType = viewModelType.BaseType!.GetGenericArguments()[0];
// If the supported type is a generic, assume there is a base type // If the supported type is a generic, assume there is a base type
if (supportedType.IsGenericParameter) if (supportedType.IsGenericParameter)
{ {
@ -198,7 +210,7 @@ namespace Artemis.UI.Shared.Services
supportedType = supportedType.BaseType; supportedType = supportedType.BaseType;
} }
PropertyInputRegistration existing = _registeredPropertyEditors.FirstOrDefault(r => r.SupportedType == supportedType); PropertyInputRegistration? existing = _registeredPropertyEditors.FirstOrDefault(r => r.SupportedType == supportedType);
if (existing != null) if (existing != null)
{ {
if (existing.Plugin != plugin) if (existing.Plugin != plugin)
@ -228,9 +240,9 @@ namespace Artemis.UI.Shared.Services
} }
} }
public TimeSpan SnapToTimeline(TimeSpan time, TimeSpan tolerance, bool snapToSegments, bool snapToCurrentTime, List<TimeSpan> snapTimes = null) public TimeSpan SnapToTimeline(TimeSpan time, TimeSpan tolerance, bool snapToSegments, bool snapToCurrentTime, List<TimeSpan>? snapTimes = null)
{ {
if (snapToSegments) if (snapToSegments && SelectedProfileElement != null)
{ {
// Snap to the end of the start segment // Snap to the end of the start segment
if (Math.Abs(time.TotalMilliseconds - SelectedProfileElement.Timeline.StartSegmentEndPosition.TotalMilliseconds) < tolerance.TotalMilliseconds) if (Math.Abs(time.TotalMilliseconds - SelectedProfileElement.Timeline.StartSegmentEndPosition.TotalMilliseconds) < tolerance.TotalMilliseconds)
@ -255,7 +267,7 @@ namespace Artemis.UI.Shared.Services
if (snapTimes != null) if (snapTimes != null)
{ {
// Find the closest keyframe // Find the closest keyframe
TimeSpan closeSnapTime = snapTimes.FirstOrDefault(s => Math.Abs(time.TotalMilliseconds - s.TotalMilliseconds) < tolerance.TotalMilliseconds); TimeSpan closeSnapTime = snapTimes.FirstOrDefault(s => Math.Abs(time.TotalMilliseconds - s.TotalMilliseconds) < tolerance.TotalMilliseconds)!;
if (closeSnapTime != TimeSpan.Zero) if (closeSnapTime != TimeSpan.Zero)
return closeSnapTime; return closeSnapTime;
} }
@ -263,10 +275,10 @@ namespace Artemis.UI.Shared.Services
return time; return time;
} }
public PropertyInputViewModel<T> CreatePropertyInputViewModel<T>(LayerProperty<T> layerProperty) public PropertyInputViewModel<T>? CreatePropertyInputViewModel<T>(LayerProperty<T> layerProperty)
{ {
Type viewModelType = null; Type? viewModelType = null;
PropertyInputRegistration registration = RegisteredPropertyEditors.FirstOrDefault(r => r.SupportedType == typeof(T)); PropertyInputRegistration? registration = RegisteredPropertyEditors.FirstOrDefault(r => r.SupportedType == typeof(T));
// Check for enums if no supported type was found // Check for enums if no supported type was found
if (registration == null && typeof(T).IsEnum) if (registration == null && typeof(T).IsEnum)
@ -281,22 +293,30 @@ namespace Artemis.UI.Shared.Services
else else
return null; return null;
if (viewModelType == null)
return null;
ConstructorArgument parameter = new ConstructorArgument("layerProperty", layerProperty); ConstructorArgument parameter = new ConstructorArgument("layerProperty", layerProperty);
IKernel kernel = registration != null ? registration.Plugin.Kernel : _kernel; // ReSharper disable once InconsistentlySynchronizedField
// When you've just spent the last 2 hours trying to figure out a deadlock and reach this line, I'm so, so sorry. I thought this would be fine.
IKernel kernel = registration?.Plugin.Kernel ?? _kernel;
return (PropertyInputViewModel<T>) kernel.Get(viewModelType, parameter); return (PropertyInputViewModel<T>) kernel.Get(viewModelType, parameter);
} }
public ProfileModule GetCurrentModule() public ProfileModule? GetCurrentModule()
{ {
return SelectedProfile?.Module; return SelectedProfile?.Module;
} }
private void ReloadProfile() private void ReloadProfile()
{ {
if (SelectedProfile == null)
return;
// Trigger a profile change // Trigger a profile change
OnSelectedProfileChanged(new ProfileEventArgs(SelectedProfile, SelectedProfile)); OnSelectedProfileChanged(new ProfileEventArgs(SelectedProfile, SelectedProfile));
// Trigger a selected element change // Trigger a selected element change
RenderProfileElement previousSelectedProfileElement = SelectedProfileElement; RenderProfileElement? previousSelectedProfileElement = SelectedProfileElement;
if (SelectedProfileElement is Folder folder) if (SelectedProfileElement is Folder folder)
SelectedProfileElement = SelectedProfile.GetAllFolders().FirstOrDefault(f => f.EntityId == folder.EntityId); SelectedProfileElement = SelectedProfile.GetAllFolders().FirstOrDefault(f => f.EntityId == folder.EntityId);
else if (SelectedProfileElement is Layer layer) else if (SelectedProfileElement is Layer layer)
@ -305,22 +325,24 @@ namespace Artemis.UI.Shared.Services
// Trigger selected data binding change // Trigger selected data binding change
if (SelectedDataBinding != null) if (SelectedDataBinding != null)
{ {
SelectedDataBinding = SelectedProfileElement?.GetAllLayerProperties()?.FirstOrDefault(p => p.Path == SelectedDataBinding.Path); SelectedDataBinding = SelectedProfileElement?.GetAllLayerProperties().FirstOrDefault(p => p.Path == SelectedDataBinding.Path);
OnSelectedDataBindingChanged(); OnSelectedDataBindingChanged();
} }
UpdateProfilePreview(); UpdateProfilePreview();
} }
public event EventHandler<ProfileEventArgs> ProfileSelected; #region Events
public event EventHandler<ProfileEventArgs> SelectedProfileUpdated;
public event EventHandler<RenderProfileElementEventArgs> ProfileElementSelected; public event EventHandler<ProfileEventArgs>? ProfileSelected;
public event EventHandler<RenderProfileElementEventArgs> SelectedProfileElementUpdated; public event EventHandler<ProfileEventArgs>? SelectedProfileUpdated;
public event EventHandler SelectedDataBindingChanged; public event EventHandler<RenderProfileElementEventArgs>? ProfileElementSelected;
public event EventHandler CurrentTimeChanged; public event EventHandler<RenderProfileElementEventArgs>? SelectedProfileElementUpdated;
public event EventHandler PixelsPerSecondChanged; public event EventHandler? SelectedDataBindingChanged;
public event EventHandler ProfilePreviewUpdated; public event EventHandler? CurrentTimeChanged;
public event EventHandler CurrentTimelineChanged; public event EventHandler? PixelsPerSecondChanged;
public event EventHandler? ProfilePreviewUpdated;
public event EventHandler? CurrentTimelineChanged;
protected virtual void OnSelectedProfileChanged(ProfileEventArgs e) protected virtual void OnSelectedProfileChanged(ProfileEventArgs e)
{ {
@ -367,10 +389,13 @@ namespace Artemis.UI.Shared.Services
SelectedDataBindingChanged?.Invoke(this, EventArgs.Empty); SelectedDataBindingChanged?.Invoke(this, EventArgs.Empty);
} }
private void SelectedProfileOnDeactivated(object sender, EventArgs e) private void SelectedProfileOnDeactivated(object? sender, EventArgs e)
{ {
// Execute.PostToUIThread(() => ChangeSelectedProfile(null)); // Execute.PostToUIThread(() => ChangeSelectedProfile(null));
ChangeSelectedProfile(null); ChangeSelectedProfile(null);
} }
#endregion
} }
} }

View File

@ -9,8 +9,8 @@ namespace Artemis.UI.Shared
/// </summary> /// </summary>
public class ShortcutUtilities public class ShortcutUtilities
{ {
private static readonly Type m_type = Type.GetTypeFromProgID("WScript.Shell"); private static readonly Type MType = Type.GetTypeFromProgID("WScript.Shell")!;
private static readonly object m_shell = Activator.CreateInstance(m_type); private static readonly object MShell = Activator.CreateInstance(MType)!;
/// <summary> /// <summary>
/// Creates a shortcut /// Creates a shortcut
@ -24,7 +24,10 @@ namespace Artemis.UI.Shared
/// <param name="iconPath">The icon path of the shortcut</param> /// <param name="iconPath">The icon path of the shortcut</param>
public static void Create(string fileName, string targetPath, string arguments, string workingDirectory, string description, string hotkey, string iconPath) public static void Create(string fileName, string targetPath, string arguments, string workingDirectory, string description, string hotkey, string iconPath)
{ {
IWshShortcut shortcut = (IWshShortcut) m_type.InvokeMember("CreateShortcut", BindingFlags.InvokeMethod, null, m_shell, new object[] {fileName}); IWshShortcut? shortcut = (IWshShortcut?) MType.InvokeMember("CreateShortcut", BindingFlags.InvokeMethod, null, MShell, new object[] {fileName});
if (shortcut == null)
throw new ArtemisSharedUIException("InvokeMember CreateShortcut returned null");
shortcut.Description = description; shortcut.Description = description;
shortcut.Hotkey = hotkey; shortcut.Hotkey = hotkey;
shortcut.TargetPath = targetPath; shortcut.TargetPath = targetPath;
@ -34,7 +37,7 @@ namespace Artemis.UI.Shared
shortcut.IconLocation = iconPath; shortcut.IconLocation = iconPath;
shortcut.Save(); shortcut.Save();
} }
[ComImport] [ComImport]
[TypeLibType(0x1040)] [TypeLibType(0x1040)]
[Guid("F935DC23-1CF0-11D0-ADB9-00C04FD58A0B")] [Guid("F935DC23-1CF0-11D0-ADB9-00C04FD58A0B")]
@ -118,14 +121,7 @@ namespace Artemis.UI.Shared
} }
[DispId(0x3ee)] [DispId(0x3ee)]
int WindowStyle int WindowStyle { [DispId(0x3ee)] get; [param: In] [DispId(0x3ee)] set; }
{
[DispId(0x3ee)]
get;
[param: In]
[DispId(0x3ee)]
set;
}
[DispId(0x3ef)] [DispId(0x3ef)]
string WorkingDirectory string WorkingDirectory
@ -141,6 +137,7 @@ namespace Artemis.UI.Shared
[TypeLibFunc(0x40)] [TypeLibFunc(0x40)]
[DispId(0x7d0)] [DispId(0x7d0)]
// ReSharper disable once InconsistentNaming - No idea if that breaks it and cba to test
void Load([In] [MarshalAs(UnmanagedType.BStr)] string PathLink); void Load([In] [MarshalAs(UnmanagedType.BStr)] string PathLink);
[DispId(0x7d1)] [DispId(0x7d1)]

View File

@ -20,12 +20,12 @@ namespace Artemis.UI.Shared
/// If not matching item can be found, /// If not matching item can be found,
/// a null parent is being returned. /// a null parent is being returned.
/// </returns> /// </returns>
public static T FindChild<T>(DependencyObject parent, string childName) where T : DependencyObject public static T? FindChild<T>(DependencyObject? parent, string? childName) where T : DependencyObject
{ {
// Confirm parent and childName are valid. // Confirm parent and childName are valid.
if (parent == null) return null; if (parent == null) return null;
T foundChild = null; T? foundChild = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent); int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++) for (int i = 0; i < childrenCount; i++)
@ -72,10 +72,10 @@ namespace Artemis.UI.Shared
/// If not matching item can be found, /// If not matching item can be found,
/// a null parent is being returned. /// a null parent is being returned.
/// </returns> /// </returns>
public static T FindParent<T>(DependencyObject child, string parentName) where T : DependencyObject public static T? FindParent<T>(DependencyObject child, string? parentName) where T : DependencyObject
{ {
// Get parent item // Get parent item
DependencyObject parentObject = VisualTreeHelper.GetParent(child); DependencyObject? parentObject = VisualTreeHelper.GetParent(child);
// We've reached the end of the tree // We've reached the end of the tree
if (parentObject == null) if (parentObject == null)

View File

@ -14,7 +14,6 @@ using Artemis.Core.Ninject;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Ninject; using Artemis.UI.Ninject;
using Artemis.UI.Screens; using Artemis.UI.Screens;
using Artemis.UI.Screens.Splash;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Artemis.UI.Stylet; using Artemis.UI.Stylet;
@ -88,7 +87,8 @@ namespace Artemis.UI
protected override void ConfigureIoC(IKernel kernel) protected override void ConfigureIoC(IKernel kernel)
{ {
// kernel.Settings.InjectNonPublic = true; // This is kinda needed for the VM factories in the Shared UI but perhaps there's a less global solution
kernel.Settings.InjectNonPublic = true;
// Load the UI modules // Load the UI modules
kernel.Load<UIModule>(); kernel.Load<UIModule>();
@ -123,7 +123,7 @@ namespace Artemis.UI
Execute.OnUIThread(() => Application.Current.Shutdown()); Execute.OnUIThread(() => Application.Current.Shutdown());
} }
private void CreateDataDirectory(ILogger logger) private static void CreateDataDirectory(ILogger logger)
{ {
// Ensure the data folder exists // Ensure the data folder exists
if (Directory.Exists(Constants.DataFolder)) if (Directory.Exists(Constants.DataFolder))
@ -161,14 +161,5 @@ namespace Artemis.UI
Environment.Exit(1); Environment.Exit(1);
}); });
} }
private void ShowMainWindow(IWindowManager windowManager, SplashViewModel splashViewModel)
{
Execute.OnUIThread(() =>
{
windowManager.ShowWindow(RootViewModel);
splashViewModel.RequestClose();
});
}
} }
} }

View File

@ -1,11 +1,12 @@
using System.Timers; using System;
using System.Timers;
using Artemis.Core.Modules; using Artemis.Core.Modules;
using Humanizer; using Humanizer;
using Stylet; using Stylet;
namespace Artemis.UI.Screens.Modules.Tabs namespace Artemis.UI.Screens.Modules.Tabs
{ {
public class ActivationRequirementViewModel : Screen public sealed class ActivationRequirementViewModel : Screen, IDisposable
{ {
private readonly IModuleActivationRequirement _activationRequirement; private readonly IModuleActivationRequirement _activationRequirement;
private readonly Timer _updateTimer; private readonly Timer _updateTimer;
@ -60,5 +61,15 @@ namespace Artemis.UI.Screens.Modules.Tabs
RequirementDescription = _activationRequirement.GetUserFriendlyDescription(); RequirementDescription = _activationRequirement.GetUserFriendlyDescription();
RequirementMet = _activationRequirement.Evaluate(); RequirementMet = _activationRequirement.Evaluate();
} }
#region IDisposable
/// <inheritdoc />
public void Dispose()
{
_updateTimer?.Dispose();
}
#endregion
} }
} }

View File

@ -5,27 +5,25 @@ using System.Windows;
using System.Windows.Media; using System.Windows.Media;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Input; using Artemis.UI.Shared.Input;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Stylet; using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.Conditions namespace Artemis.UI.Screens.ProfileEditor.Conditions.Abstract
{ {
public abstract class DataModelConditionPredicateViewModel : DataModelConditionViewModel, IDisposable public abstract class DataModelConditionPredicateViewModel : DataModelConditionViewModel, IDisposable
{ {
private readonly IConditionOperatorService _conditionOperatorService; private readonly IConditionOperatorService _conditionOperatorService;
private readonly IDataModelUIService _dataModelUIService; private readonly IDataModelUIService _dataModelUIService;
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private BindableCollection<BaseConditionOperator> _operators;
private DataModelStaticViewModel _rightSideInputViewModel; private DataModelStaticViewModel _rightSideInputViewModel;
private DataModelDynamicViewModel _rightSideSelectionViewModel; private DataModelDynamicViewModel _rightSideSelectionViewModel;
private BaseConditionOperator _selectedOperator; private BaseConditionOperator _selectedOperator;
private List<Type> _supportedInputTypes; private List<Type> _supportedInputTypes;
public DataModelConditionPredicateViewModel( protected DataModelConditionPredicateViewModel(
DataModelConditionPredicate dataModelConditionPredicate, DataModelConditionPredicate dataModelConditionPredicate,
IProfileEditorService profileEditorService, IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService, IDataModelUIService dataModelUIService,
@ -199,17 +197,27 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
#region IDisposable #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() public void Dispose()
{ {
if (LeftSideSelectionViewModel != null) Dispose(true);
{ GC.SuppressFinalize(this);
LeftSideSelectionViewModel.PropertySelected -= LeftSideOnPropertySelected;
LeftSideSelectionViewModel.Dispose();
LeftSideSelectionViewModel = null;
}
DisposeRightSideStaticViewModel();
DisposeRightSideDynamicViewModel();
} }
#endregion #endregion
@ -284,7 +292,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
Update(); Update();
} }
private void RightSideInputViewModelOnSwitchToDynamicRequested(object? sender, EventArgs e) private void RightSideInputViewModelOnSwitchToDynamicRequested(object sender, EventArgs e)
{ {
DataModelConditionPredicate.PredicateType = ProfileRightSideType.Dynamic; DataModelConditionPredicate.PredicateType = ProfileRightSideType.Dynamic;
Update(); Update();

View File

@ -10,7 +10,7 @@ using Artemis.UI.Shared.Services;
namespace Artemis.UI.Screens.ProfileEditor.Conditions namespace Artemis.UI.Screens.ProfileEditor.Conditions
{ {
public class DataModelConditionEventViewModel : DataModelConditionViewModel, IDisposable public sealed class DataModelConditionEventViewModel : DataModelConditionViewModel, IDisposable
{ {
private readonly IDataModelConditionsVmFactory _dataModelConditionsVmFactory; private readonly IDataModelConditionsVmFactory _dataModelConditionsVmFactory;
private readonly IDataModelUIService _dataModelUIService; private readonly IDataModelUIService _dataModelUIService;
@ -90,7 +90,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
#region Event handlers #region Event handlers
private void LeftSideSelectionViewModelOnPropertySelected(object? sender, DataModelInputDynamicEventArgs e) private void LeftSideSelectionViewModelOnPropertySelected(object sender, DataModelInputDynamicEventArgs e)
{ {
ApplyEvent(); ApplyEvent();
} }
@ -104,7 +104,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
LeftSideSelectionViewModel.Dispose(); LeftSideSelectionViewModel.Dispose();
LeftSideSelectionViewModel.PropertySelected -= LeftSideSelectionViewModelOnPropertySelected; LeftSideSelectionViewModel.PropertySelected -= LeftSideSelectionViewModelOnPropertySelected;
} }
#endregion #endregion
} }
} }

View File

@ -11,7 +11,7 @@ using Humanizer;
namespace Artemis.UI.Screens.ProfileEditor.Conditions namespace Artemis.UI.Screens.ProfileEditor.Conditions
{ {
public class DataModelConditionListViewModel : DataModelConditionViewModel, IDisposable public sealed class DataModelConditionListViewModel : DataModelConditionViewModel, IDisposable
{ {
private readonly IDataModelConditionsVmFactory _dataModelConditionsVmFactory; private readonly IDataModelConditionsVmFactory _dataModelConditionsVmFactory;
private readonly IDataModelUIService _dataModelUIService; private readonly IDataModelUIService _dataModelUIService;
@ -92,7 +92,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
Update(); Update();
} }
public override void Update() public override void Update()
{ {
LeftSideSelectionViewModel.ChangeDataModelPath(DataModelConditionList.ListPath); LeftSideSelectionViewModel.ChangeDataModelPath(DataModelConditionList.ListPath);
@ -130,15 +130,19 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
base.OnInitialActivate(); base.OnInitialActivate();
} }
private void LeftSideSelectionViewModelOnPropertySelected(object? sender, DataModelInputDynamicEventArgs e) private void LeftSideSelectionViewModelOnPropertySelected(object sender, DataModelInputDynamicEventArgs e)
{ {
ApplyList(); ApplyList();
} }
#region IDisposable
public void Dispose() public void Dispose()
{ {
LeftSideSelectionViewModel.Dispose(); LeftSideSelectionViewModel.Dispose();
LeftSideSelectionViewModel.PropertySelected -= LeftSideSelectionViewModelOnPropertySelected; LeftSideSelectionViewModel.PropertySelected -= LeftSideSelectionViewModelOnPropertySelected;
} }
#endregion
} }
} }

View File

@ -5,6 +5,7 @@ using System.Windows.Media;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Extensions; using Artemis.UI.Extensions;
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;

View File

@ -5,6 +5,7 @@ using System.Windows.Media;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Extensions; using Artemis.UI.Extensions;
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;

View File

@ -114,7 +114,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
RenderProfileElement.DisplayCondition.ChildRemoved += DisplayConditionOnChildrenModified; RenderProfileElement.DisplayCondition.ChildRemoved += DisplayConditionOnChildrenModified;
} }
private void DisplayConditionOnChildrenModified(object? sender, EventArgs e) private void DisplayConditionOnChildrenModified(object sender, EventArgs e)
{ {
DisplayStartHint = !RenderProfileElement.DisplayCondition.Children.Any(); DisplayStartHint = !RenderProfileElement.DisplayCondition.Children.Any();
IsEventCondition = RenderProfileElement.DisplayCondition.Children.Any(c => c is DataModelConditionEvent); IsEventCondition = RenderProfileElement.DisplayCondition.Children.Any(c => c is DataModelConditionEvent);

View File

@ -10,7 +10,7 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.ConditionalDataBinding namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.ConditionalDataBinding
{ {
public class ConditionalDataBindingModeViewModel<TLayerProperty, TProperty> : Conductor<DataBindingConditionViewModel<TLayerProperty, TProperty>>.Collection.AllActive, IDataBindingModeViewModel public sealed class ConditionalDataBindingModeViewModel<TLayerProperty, TProperty> : Conductor<DataBindingConditionViewModel<TLayerProperty, TProperty>>.Collection.AllActive, IDataBindingModeViewModel
{ {
private readonly IDataBindingsVmFactory _dataBindingsVmFactory; private readonly IDataBindingsVmFactory _dataBindingsVmFactory;
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
@ -109,7 +109,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.Conditio
} }
#region IDisposable #region IDisposable
/// <inheritdoc />
public void Dispose() public void Dispose()
{ {
ConditionalDataBinding.ConditionsUpdated -= ConditionalDataBindingOnConditionsUpdated; ConditionalDataBinding.ConditionsUpdated -= ConditionalDataBindingOnConditionsUpdated;

View File

@ -10,11 +10,11 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.ConditionalDataBinding namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.ConditionalDataBinding
{ {
public class DataBindingConditionViewModel<TLayerProperty, TProperty> : Conductor<DataModelConditionGroupViewModel>, IDisposable public sealed class DataBindingConditionViewModel<TLayerProperty, TProperty> : Conductor<DataModelConditionGroupViewModel>, IDisposable
{ {
private readonly IProfileEditorService _profileEditorService;
private readonly IDataModelConditionsVmFactory _dataModelConditionsVmFactory; private readonly IDataModelConditionsVmFactory _dataModelConditionsVmFactory;
private readonly IDataModelUIService _dataModelUIService; private readonly IDataModelUIService _dataModelUIService;
private readonly IProfileEditorService _profileEditorService;
public DataBindingConditionViewModel(DataBindingCondition<TLayerProperty, TProperty> dataBindingCondition, public DataBindingConditionViewModel(DataBindingCondition<TLayerProperty, TProperty> dataBindingCondition,
IProfileEditorService profileEditorService, IProfileEditorService profileEditorService,
@ -44,11 +44,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.Conditio
ValueViewModel.Value = DataBindingCondition.Value; ValueViewModel.Value = DataBindingCondition.Value;
} }
public void Dispose()
{
ValueViewModel.Dispose();
}
private void ActiveItemOnUpdated(object sender, EventArgs e) private void ActiveItemOnUpdated(object sender, EventArgs e)
{ {
if (!ActiveItem.GetChildren().Any()) if (!ActiveItem.GetChildren().Any())
@ -60,5 +55,15 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.Conditio
DataBindingCondition.Value = (TProperty) Convert.ChangeType(e.Value, typeof(TProperty)); DataBindingCondition.Value = (TProperty) Convert.ChangeType(e.Value, typeof(TProperty));
_profileEditorService.UpdateSelectedProfileElement(); _profileEditorService.UpdateSelectedProfileElement();
} }
#region IDisposable
/// <inheritdoc />
public void Dispose()
{
ValueViewModel?.Dispose();
}
#endregion
} }
} }

View File

@ -10,17 +10,17 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
{ {
public class DataBindingViewModel<TLayerProperty, TProperty> : Conductor<IDataBindingModeViewModel>, IDataBindingViewModel public sealed class DataBindingViewModel<TLayerProperty, TProperty> : Conductor<IDataBindingModeViewModel>, IDataBindingViewModel
{ {
private readonly IDataBindingsVmFactory _dataBindingsVmFactory; private readonly IDataBindingsVmFactory _dataBindingsVmFactory;
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private readonly Timer _updateTimer; private readonly Timer _updateTimer;
private bool _applyTestResultToLayer;
private int _easingTime; private int _easingTime;
private bool _isDataBindingEnabled; private bool _isDataBindingEnabled;
private bool _isEasingTimeEnabled; private bool _isEasingTimeEnabled;
private DataBindingModeType _selectedDataBindingMode; private DataBindingModeType _selectedDataBindingMode;
private TimelineEasingViewModel _selectedEasingViewModel; private TimelineEasingViewModel _selectedEasingViewModel;
private bool _applyTestResultToLayer;
private bool _updating; private bool _updating;
private bool _updatingTestResult; private bool _updatingTestResult;
@ -46,12 +46,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
TestResultValue = dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), null, true); TestResultValue = dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), null, true);
} }
protected override void OnInitialActivate()
{
base.OnInitialActivate();
Initialize();
}
public DataBindingRegistration<TLayerProperty, TProperty> Registration { get; } public DataBindingRegistration<TLayerProperty, TProperty> Registration { get; }
public BindableCollection<ValueDescription> DataBindingModes { get; } public BindableCollection<ValueDescription> DataBindingModes { get; }
@ -115,12 +109,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
} }
} }
public void Dispose() protected override void OnInitialActivate()
{ {
_updateTimer.Dispose(); base.OnInitialActivate();
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed; Initialize();
Registration.LayerProperty.Updated -= LayerPropertyOnUpdated;
} }
private void Initialize() private void Initialize()
@ -171,18 +163,12 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
EasingTime = (int) Registration.DataBinding.EasingTime.TotalMilliseconds; EasingTime = (int) Registration.DataBinding.EasingTime.TotalMilliseconds;
SelectedEasingViewModel = EasingViewModels.First(vm => vm.EasingFunction == Registration.DataBinding.EasingFunction); SelectedEasingViewModel = EasingViewModels.First(vm => vm.EasingFunction == Registration.DataBinding.EasingFunction);
IsEasingTimeEnabled = EasingTime > 0; IsEasingTimeEnabled = EasingTime > 0;
switch (Registration.DataBinding.DataBindingMode) SelectedDataBindingMode = Registration.DataBinding.DataBindingMode switch
{ {
case DirectDataBinding<TLayerProperty, TProperty> _: DirectDataBinding<TLayerProperty, TProperty> _ => DataBindingModeType.Direct,
SelectedDataBindingMode = DataBindingModeType.Direct; ConditionalDataBinding<TLayerProperty, TProperty> _ => DataBindingModeType.Conditional,
break; _ => DataBindingModeType.None
case ConditionalDataBinding<TLayerProperty, TProperty> _: };
SelectedDataBindingMode = DataBindingModeType.Conditional;
break;
default:
SelectedDataBindingMode = DataBindingModeType.None;
break;
}
ActiveItem?.Update(); ActiveItem?.Update();
@ -250,7 +236,9 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
TestResultValue.UpdateValue(Registration.DataBinding != null ? Registration.DataBinding.GetValue(currentValue) : default); TestResultValue.UpdateValue(Registration.DataBinding != null ? Registration.DataBinding.GetValue(currentValue) : default);
} }
else else
{
TestResultValue.UpdateValue(Registration.DataBinding != null ? Registration.DataBinding.GetValue(default) : default); TestResultValue.UpdateValue(Registration.DataBinding != null ? Registration.DataBinding.GetValue(default) : default);
}
if (ApplyTestResultToLayer) if (ApplyTestResultToLayer)
{ {
@ -288,11 +276,24 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
UpdateTestResult(); UpdateTestResult();
} }
private void LayerPropertyOnUpdated(object? sender, LayerPropertyEventArgs<TLayerProperty> e) private void LayerPropertyOnUpdated(object sender, LayerPropertyEventArgs<TLayerProperty> e)
{ {
if (ApplyTestResultToLayer) if (ApplyTestResultToLayer)
Registration.DataBinding?.Apply(); Registration.DataBinding?.Apply();
} }
#region IDisposable
/// <inheritdoc />
public void Dispose()
{
_updateTimer.Dispose();
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
Registration.LayerProperty.Updated -= LayerPropertyOnUpdated;
}
#endregion
} }
public interface IDataBindingViewModel : IScreen, IDisposable public interface IDataBindingViewModel : IScreen, IDisposable

View File

@ -52,7 +52,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
SelectedItemIndex = 0; SelectedItemIndex = 0;
} }
private void ProfileEditorServiceOnSelectedDataBindingChanged(object? sender, EventArgs e) private void ProfileEditorServiceOnSelectedDataBindingChanged(object sender, EventArgs e)
{ {
CreateDataBindingViewModels(); CreateDataBindingViewModels();
} }

View File

@ -12,7 +12,7 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDataBinding namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDataBinding
{ {
public class DataBindingModifierViewModel<TLayerProperty, TProperty> : PropertyChangedBase, IDisposable public sealed class DataBindingModifierViewModel<TLayerProperty, TProperty> : PropertyChangedBase, IDisposable
{ {
private readonly IDataBindingService _dataBindingService; private readonly IDataBindingService _dataBindingService;
private readonly IDataModelUIService _dataModelUIService; private readonly IDataModelUIService _dataModelUIService;
@ -161,6 +161,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa
#region IDisposable #region IDisposable
/// <inheritdoc />
public void Dispose() public void Dispose()
{ {
DisposeDynamicSelectionViewModel(); DisposeDynamicSelectionViewModel();
@ -193,13 +194,13 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa
#region Event handlers #region Event handlers
private void DynamicSelectionViewModelOnSwitchToStaticRequested(object? sender, EventArgs e) private void DynamicSelectionViewModelOnSwitchToStaticRequested(object sender, EventArgs e)
{ {
Modifier.ParameterType = ProfileRightSideType.Static; Modifier.ParameterType = ProfileRightSideType.Static;
Update(); Update();
} }
private void StaticInputViewModelOnSwitchToDynamicRequested(object? sender, EventArgs e) private void StaticInputViewModelOnSwitchToDynamicRequested(object sender, EventArgs e)
{ {
Modifier.ParameterType = ProfileRightSideType.Dynamic; Modifier.ParameterType = ProfileRightSideType.Dynamic;
Update(); Update();

View File

@ -12,7 +12,7 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDataBinding namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDataBinding
{ {
public class DirectDataBindingModeViewModel<TLayerProperty, TProperty> : Screen, IDataBindingModeViewModel public sealed class DirectDataBindingModeViewModel<TLayerProperty, TProperty> : Screen, IDataBindingModeViewModel
{ {
private readonly IDataBindingsVmFactory _dataBindingsVmFactory; private readonly IDataBindingsVmFactory _dataBindingsVmFactory;
private readonly IDataModelUIService _dataModelUIService; private readonly IDataModelUIService _dataModelUIService;
@ -70,6 +70,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa
#region IDisposable #region IDisposable
/// <inheritdoc />
public void Dispose() public void Dispose()
{ {
TargetSelectionViewModel.PropertySelected -= TargetSelectionViewModelOnPropertySelected; TargetSelectionViewModel.PropertySelected -= TargetSelectionViewModelOnPropertySelected;

View File

@ -9,7 +9,7 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
{ {
public class LayerPropertyGroupViewModel : PropertyChangedBase, IDisposable public sealed class LayerPropertyGroupViewModel : PropertyChangedBase, IDisposable
{ {
private readonly ILayerPropertyVmFactory _layerPropertyVmFactory; private readonly ILayerPropertyVmFactory _layerPropertyVmFactory;
private bool _isVisible; private bool _isVisible;
@ -40,9 +40,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
get => _isVisible; get => _isVisible;
set => SetAndNotify(ref _isVisible, value); set => SetAndNotify(ref _isVisible, value);
} }
public bool IsHighlighted => false;
public bool IsExpanded public bool IsExpanded
{ {
get => LayerPropertyGroup.ProfileElement.IsPropertyGroupExpanded(LayerPropertyGroup); get => LayerPropertyGroup.ProfileElement.IsPropertyGroupExpanded(LayerPropertyGroup);
@ -53,6 +51,9 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
} }
} }
#region IDisposable
/// <inheritdoc />
public void Dispose() public void Dispose()
{ {
TimelineGroupViewModel.Dispose(); TimelineGroupViewModel.Dispose();
@ -64,6 +65,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
} }
} }
#endregion
public void UpdateOrder(int order) public void UpdateOrder(int order)
{ {
LayerPropertyGroup.LayerEffect.Order = order; LayerPropertyGroup.LayerEffect.Order = order;
@ -93,7 +96,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
/// </summary> /// </summary>
/// <param name="start">The position at which to start removing keyframes, if null this will start at the first keyframe</param> /// <param name="start">The position at which to start removing keyframes, if null this will start at the first keyframe</param>
/// <param name="end">The position at which to start removing keyframes, if null this will end at the last keyframe</param> /// <param name="end">The position at which to start removing keyframes, if null this will end at the last keyframe</param>
public virtual void WipeKeyframes(TimeSpan? start, TimeSpan? end) public void WipeKeyframes(TimeSpan? start, TimeSpan? end)
{ {
foreach (PropertyChangedBase child in Children) foreach (PropertyChangedBase child in Children)
{ {
@ -139,7 +142,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
{ {
PropertyDescriptionAttribute propertyAttribute = (PropertyDescriptionAttribute) Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyDescriptionAttribute)); PropertyDescriptionAttribute propertyAttribute = (PropertyDescriptionAttribute) Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyDescriptionAttribute));
PropertyGroupDescriptionAttribute groupAttribute = (PropertyGroupDescriptionAttribute) Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyGroupDescriptionAttribute)); PropertyGroupDescriptionAttribute groupAttribute = (PropertyGroupDescriptionAttribute) Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyGroupDescriptionAttribute));
object? value = propertyInfo.GetValue(LayerPropertyGroup); object value = propertyInfo.GetValue(LayerPropertyGroup);
// Create VMs for properties on the group // Create VMs for properties on the group
if (propertyAttribute != null && value is ILayerProperty layerProperty) if (propertyAttribute != null && value is ILayerProperty layerProperty)

View File

@ -7,7 +7,7 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
{ {
public class LayerPropertyViewModel : PropertyChangedBase, IDisposable public sealed class LayerPropertyViewModel : PropertyChangedBase, IDisposable
{ {
private bool _isVisible; private bool _isVisible;
private bool _isHighlighted; private bool _isHighlighted;

View File

@ -7,7 +7,7 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
{ {
public class TimelineGroupViewModel : PropertyChangedBase, IDisposable public sealed class TimelineGroupViewModel : PropertyChangedBase, IDisposable
{ {
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;

View File

@ -7,11 +7,10 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
{ {
public class TimelineKeyframeViewModel<T> : Screen, ITimelineKeyframeViewModel public sealed class TimelineKeyframeViewModel<T> : Screen, ITimelineKeyframeViewModel
{ {
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private BindableCollection<TimelineEasingViewModel> _easingViewModels;
private bool _isSelected; private bool _isSelected;
private string _timestamp; private string _timestamp;
private double _x; private double _x;
@ -65,7 +64,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
Timestamp = $"{Math.Floor(LayerPropertyKeyframe.Position.TotalSeconds):00}.{LayerPropertyKeyframe.Position.Milliseconds:000}"; Timestamp = $"{Math.Floor(LayerPropertyKeyframe.Position.TotalSeconds):00}.{LayerPropertyKeyframe.Position.Milliseconds:000}";
} }
private void ProfileEditorServiceOnPixelsPerSecondChanged(object? sender, EventArgs e) private void ProfileEditorServiceOnPixelsPerSecondChanged(object sender, EventArgs e)
{ {
Update(); Update();
} }

View File

@ -7,7 +7,7 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
{ {
public class TimelinePropertyViewModel<T> : Conductor<TimelineKeyframeViewModel<T>>.Collection.AllActive, ITimelinePropertyViewModel public sealed class TimelinePropertyViewModel<T> : Conductor<TimelineKeyframeViewModel<T>>.Collection.AllActive, ITimelinePropertyViewModel
{ {
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private double _width; private double _width;

View File

@ -7,7 +7,6 @@ using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Screens.ProfileEditor.Dialogs;
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline.Dialogs; using Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline.Dialogs;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
@ -15,19 +14,19 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
{ {
public class TimelineSegmentViewModel : Screen, IDisposable public sealed class TimelineSegmentViewModel : Screen, IDisposable
{ {
private readonly IDialogService _dialogService; private readonly IDialogService _dialogService;
private bool _draggingSegment; private bool _draggingSegment;
private bool _segmentEnabled;
private TimeSpan _segmentEnd;
private TimeSpan _segmentLength;
private TimeSpan _segmentStart;
private double _segmentStartPosition;
private double _segmentWidth;
private bool _showDisableButton; private bool _showDisableButton;
private bool _showRepeatButton; private bool _showRepeatButton;
private bool _showSegmentName; private bool _showSegmentName;
private TimeSpan _segmentLength;
private TimeSpan _segmentStart;
private TimeSpan _segmentEnd;
private double _segmentWidth;
private bool _segmentEnabled;
private double _segmentStartPosition;
public TimelineSegmentViewModel(SegmentViewModelType segment, BindableCollection<LayerPropertyGroupViewModel> layerPropertyGroups, public TimelineSegmentViewModel(SegmentViewModelType segment, BindableCollection<LayerPropertyGroupViewModel> layerPropertyGroups,
IProfileEditorService profileEditorService, IDialogService dialogService) IProfileEditorService profileEditorService, IDialogService dialogService)
@ -139,6 +138,40 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
await _dialogService.ShowDialog<TimelineSegmentDialogViewModel>(new Dictionary<string, object> {{"segment", this}}); await _dialogService.ShowDialog<TimelineSegmentDialogViewModel>(new Dictionary<string, object> {{"segment", this}});
} }
public void ShiftNextSegment(TimeSpan amount)
{
if (Segment == SegmentViewModelType.Start)
ShiftKeyframes(SelectedProfileElement.Timeline.StartSegmentEndPosition, null, amount);
else if (Segment == SegmentViewModelType.Main)
ShiftKeyframes(SelectedProfileElement.Timeline.MainSegmentEndPosition, null, amount);
else if (Segment == SegmentViewModelType.End)
ShiftKeyframes(SelectedProfileElement.Timeline.EndSegmentEndPosition, null, amount);
}
private void WipeKeyframes(TimeSpan? start, TimeSpan? end)
{
foreach (LayerPropertyGroupViewModel layerPropertyGroupViewModel in LayerPropertyGroups)
layerPropertyGroupViewModel.WipeKeyframes(start, end);
}
private void ShiftKeyframes(TimeSpan? start, TimeSpan? end, TimeSpan amount)
{
foreach (LayerPropertyGroupViewModel layerPropertyGroupViewModel in LayerPropertyGroups)
layerPropertyGroupViewModel.ShiftKeyframes(start, end, amount);
}
#region IDIsposable
public void Dispose()
{
ProfileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged;
ProfileEditorService.ProfileElementSelected -= ProfileEditorServiceOnProfileElementSelected;
if (SelectedProfileElement != null)
SelectedProfileElement.Timeline.PropertyChanged -= TimelineOnPropertyChanged;
}
#endregion
#region Updating #region Updating
private void UpdateHeader() private void UpdateHeader()
@ -283,7 +316,9 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
} }
// If holding down control, round to the closest 50ms // If holding down control, round to the closest 50ms
else if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) else if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
{
newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds / 50.0) * 50.0); newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds / 50.0) * 50.0);
}
UpdateLength(newTime); UpdateLength(newTime);
} }
@ -304,7 +339,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
SelectedProfileElement.Timeline.MainSegmentEndPosition = newTime; SelectedProfileElement.Timeline.MainSegmentEndPosition = newTime;
else if (Segment == SegmentViewModelType.End) else if (Segment == SegmentViewModelType.End)
SelectedProfileElement.Timeline.EndSegmentEndPosition = newTime; SelectedProfileElement.Timeline.EndSegmentEndPosition = newTime;
if (difference < TimeSpan.Zero) if (difference < TimeSpan.Zero)
ShiftNextSegment(difference); ShiftNextSegment(difference);
@ -313,21 +348,9 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
#endregion #endregion
#region IDIsposable
public void Dispose()
{
ProfileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged;
ProfileEditorService.ProfileElementSelected -= ProfileEditorServiceOnProfileElementSelected;
if (SelectedProfileElement != null)
SelectedProfileElement.Timeline.PropertyChanged -= TimelineOnPropertyChanged;
}
#endregion
#region Event handlers #region Event handlers
private void ProfileEditorServiceOnProfileElementSelected(object? sender, RenderProfileElementEventArgs e) private void ProfileEditorServiceOnProfileElementSelected(object sender, RenderProfileElementEventArgs e)
{ {
if (e.PreviousRenderProfileElement != null) if (e.PreviousRenderProfileElement != null)
e.PreviousRenderProfileElement.Timeline.PropertyChanged -= TimelineOnPropertyChanged; e.PreviousRenderProfileElement.Timeline.PropertyChanged -= TimelineOnPropertyChanged;
@ -347,34 +370,12 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
NotifyOfPropertyChange(nameof(RepeatSegment)); NotifyOfPropertyChange(nameof(RepeatSegment));
} }
private void ProfileEditorServiceOnPixelsPerSecondChanged(object? sender, EventArgs e) private void ProfileEditorServiceOnPixelsPerSecondChanged(object sender, EventArgs e)
{ {
Update(); Update();
} }
#endregion #endregion
public void ShiftNextSegment(TimeSpan amount)
{
if (Segment == SegmentViewModelType.Start)
ShiftKeyframes(SelectedProfileElement.Timeline.StartSegmentEndPosition, null, amount);
else if (Segment == SegmentViewModelType.Main)
ShiftKeyframes(SelectedProfileElement.Timeline.MainSegmentEndPosition, null, amount);
else if (Segment == SegmentViewModelType.End)
ShiftKeyframes(SelectedProfileElement.Timeline.EndSegmentEndPosition, null, amount);
}
private void WipeKeyframes(TimeSpan? start, TimeSpan? end)
{
foreach (LayerPropertyGroupViewModel layerPropertyGroupViewModel in LayerPropertyGroups)
layerPropertyGroupViewModel.WipeKeyframes(start, end);
}
private void ShiftKeyframes(TimeSpan? start, TimeSpan? end, TimeSpan amount)
{
foreach (LayerPropertyGroupViewModel layerPropertyGroupViewModel in LayerPropertyGroups)
layerPropertyGroupViewModel.ShiftKeyframes(start, end, amount);
}
} }
public enum SegmentViewModelType public enum SegmentViewModelType

View File

@ -6,14 +6,13 @@ using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Shapes; using System.Windows.Shapes;
using Artemis.Core;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Stylet; using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
{ {
public class TimelineViewModel : Screen, IDisposable public sealed class TimelineViewModel : Screen, IDisposable
{ {
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private RectangleGeometry _selectionRectangle; private RectangleGeometry _selectionRectangle;
@ -88,7 +87,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
Update(); Update();
} }
private void ProfileEditorServiceOnProfileElementSelected(object? sender, RenderProfileElementEventArgs e) private void ProfileEditorServiceOnProfileElementSelected(object sender, RenderProfileElementEventArgs e)
{ {
if (e.PreviousRenderProfileElement != null) if (e.PreviousRenderProfileElement != null)
e.PreviousRenderProfileElement.Timeline.PropertyChanged -= TimelineOnPropertyChanged; e.PreviousRenderProfileElement.Timeline.PropertyChanged -= TimelineOnPropertyChanged;
@ -98,6 +97,18 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
Update(); Update();
} }
#region IDisposable
public void Dispose()
{
_profileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged;
_profileEditorService.ProfileElementSelected -= ProfileEditorServiceOnProfileElementSelected;
if (_profileEditorService.SelectedProfileElement != null)
_profileEditorService.SelectedProfileElement.Timeline.PropertyChanged -= TimelineOnPropertyChanged;
}
#endregion
#region Command handlers #region Command handlers
public void KeyframeMouseDown(object sender, MouseButtonEventArgs e) public void KeyframeMouseDown(object sender, MouseButtonEventArgs e)
@ -105,8 +116,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
// if (e.LeftButton == MouseButtonState.Released) // if (e.LeftButton == MouseButtonState.Released)
// return; // return;
ITimelineKeyframeViewModel viewModel = (sender as Ellipse)?.DataContext as ITimelineKeyframeViewModel; if (!((sender as Ellipse)?.DataContext is ITimelineKeyframeViewModel viewModel))
if (viewModel == null)
return; return;
((IInputElement) sender).CaptureMouse(); ((IInputElement) sender).CaptureMouse();
@ -206,7 +216,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
keyframeViewModel.SaveOffsetToKeyframe(sourceKeyframeViewModel); keyframeViewModel.SaveOffsetToKeyframe(sourceKeyframeViewModel);
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
{
cursorTime = _profileEditorService.SnapToTimeline( cursorTime = _profileEditorService.SnapToTimeline(
cursorTime, cursorTime,
TimeSpan.FromMilliseconds(1000f / _profileEditorService.PixelsPerSecond * 5), TimeSpan.FromMilliseconds(1000f / _profileEditorService.PixelsPerSecond * 5),
@ -214,7 +223,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
false, false,
keyframeViewModels.Where(k => k != sourceKeyframeViewModel).Select(k => k.Position).ToList() keyframeViewModels.Where(k => k != sourceKeyframeViewModel).Select(k => k.Position).ToList()
); );
}
sourceKeyframeViewModel.UpdatePosition(cursorTime); sourceKeyframeViewModel.UpdatePosition(cursorTime);
@ -306,15 +314,11 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
int clickedIndex = keyframeViewModels.IndexOf(clicked); int clickedIndex = keyframeViewModels.IndexOf(clicked);
if (clickedIndex < selectedIndex) if (clickedIndex < selectedIndex)
{
foreach (ITimelineKeyframeViewModel keyframeViewModel in keyframeViewModels.Skip(clickedIndex).Take(selectedIndex - clickedIndex + 1)) foreach (ITimelineKeyframeViewModel keyframeViewModel in keyframeViewModels.Skip(clickedIndex).Take(selectedIndex - clickedIndex + 1))
keyframeViewModel.IsSelected = true; keyframeViewModel.IsSelected = true;
}
else else
{
foreach (ITimelineKeyframeViewModel keyframeViewModel in keyframeViewModels.Skip(selectedIndex).Take(clickedIndex - selectedIndex + 1)) foreach (ITimelineKeyframeViewModel keyframeViewModel in keyframeViewModels.Skip(selectedIndex).Take(clickedIndex - selectedIndex + 1))
keyframeViewModel.IsSelected = true; keyframeViewModel.IsSelected = true;
}
} }
else if (toggle) else if (toggle)
{ {
@ -340,17 +344,5 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
} }
#endregion #endregion
#region IDisposable
public void Dispose()
{
_profileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged;
_profileEditorService.ProfileElementSelected -= ProfileEditorServiceOnProfileElementSelected;
if (_profileEditorService.SelectedProfileElement != null)
_profileEditorService.SelectedProfileElement.Timeline.PropertyChanged -= TimelineOnPropertyChanged;
}
#endregion
} }
} }

View File

@ -1,5 +1,4 @@
using System; using System;
using System.ComponentModel;
using System.Linq; using System.Linq;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Shared; using Artemis.UI.Shared;
@ -8,7 +7,7 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
{ {
public class TreePropertyViewModel<T> : Screen, ITreePropertyViewModel public sealed class TreePropertyViewModel<T> : Screen, ITreePropertyViewModel
{ {
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private PropertyInputViewModel<T> _propertyInputViewModel; private PropertyInputViewModel<T> _propertyInputViewModel;
@ -26,7 +25,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
LayerProperty.DataBindingDisabled += LayerPropertyOnDataBindingChange; LayerProperty.DataBindingDisabled += LayerPropertyOnDataBindingChange;
LayerPropertyViewModel.IsVisible = !LayerProperty.IsHidden; LayerPropertyViewModel.IsVisible = !LayerProperty.IsHidden;
} }
public LayerProperty<T> LayerProperty { get; } public LayerProperty<T> LayerProperty { get; }
public LayerPropertyViewModel LayerPropertyViewModel { get; } public LayerPropertyViewModel LayerPropertyViewModel { get; }
@ -36,22 +35,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
set => SetAndNotify(ref _propertyInputViewModel, value); set => SetAndNotify(ref _propertyInputViewModel, value);
} }
public bool HasDataBinding => LayerProperty.HasDataBinding;
public double GetDepth()
{
int depth = 0;
LayerPropertyGroup current = LayerProperty.LayerPropertyGroup;
while (current != null)
{
depth++;
current = current.Parent;
}
return depth;
}
public bool HasPropertyInputViewModel => PropertyInputViewModel != null;
public bool KeyframesEnabled public bool KeyframesEnabled
{ {
get => LayerProperty.KeyframesEnabled; get => LayerProperty.KeyframesEnabled;
@ -66,6 +49,42 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
_profileEditorService.ChangeSelectedDataBinding(LayerProperty); _profileEditorService.ChangeSelectedDataBinding(LayerProperty);
} }
private void ApplyKeyframesEnabled(bool enable)
{
// If enabling keyframes for the first time, add a keyframe with the current value at the current position
if (enable && !LayerProperty.Keyframes.Any())
LayerProperty.AddKeyframe(new LayerPropertyKeyframe<T>(
LayerProperty.CurrentValue,
_profileEditorService.CurrentTime,
Easings.Functions.Linear,
LayerProperty
));
// If disabling keyframes, set the base value to the current value
else if (!enable && LayerProperty.Keyframes.Any())
LayerProperty.BaseValue = LayerProperty.CurrentValue;
LayerProperty.KeyframesEnabled = enable;
_profileEditorService.UpdateSelectedProfileElement();
}
public bool HasDataBinding => LayerProperty.HasDataBinding;
public double GetDepth()
{
int depth = 0;
LayerPropertyGroup current = LayerProperty.LayerPropertyGroup;
while (current != null)
{
depth++;
current = current.Parent;
}
return depth;
}
public bool HasPropertyInputViewModel => PropertyInputViewModel != null;
#region IDisposable #region IDisposable
public void Dispose() public void Dispose()
@ -79,27 +98,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
#endregion #endregion
private void ApplyKeyframesEnabled(bool enable)
{
// If enabling keyframes for the first time, add a keyframe with the current value at the current position
if (enable && !LayerProperty.Keyframes.Any())
{
LayerProperty.AddKeyframe(new LayerPropertyKeyframe<T>(
LayerProperty.CurrentValue,
_profileEditorService.CurrentTime,
Easings.Functions.Linear,
LayerProperty
));
}
// If disabling keyframes, set the base value to the current value
else if (!enable && LayerProperty.Keyframes.Any())
LayerProperty.BaseValue = LayerProperty.CurrentValue;
LayerProperty.KeyframesEnabled = enable;
_profileEditorService.UpdateSelectedProfileElement();
}
#region Event handlers #region Event handlers
private void ProfileEditorServiceOnSelectedDataBindingChanged(object sender, EventArgs e) private void ProfileEditorServiceOnSelectedDataBindingChanged(object sender, EventArgs e)
@ -107,12 +105,12 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
LayerPropertyViewModel.IsHighlighted = _profileEditorService.SelectedDataBinding == LayerProperty; LayerPropertyViewModel.IsHighlighted = _profileEditorService.SelectedDataBinding == LayerProperty;
} }
private void LayerPropertyOnVisibilityChanged(object? sender, LayerPropertyEventArgs<T> e) private void LayerPropertyOnVisibilityChanged(object sender, LayerPropertyEventArgs<T> e)
{ {
LayerPropertyViewModel.IsVisible = !LayerProperty.IsHidden; LayerPropertyViewModel.IsVisible = !LayerProperty.IsHidden;
} }
private void LayerPropertyOnDataBindingChange(object? sender, LayerPropertyEventArgs<T> e) private void LayerPropertyOnDataBindingChange(object sender, LayerPropertyEventArgs<T> e)
{ {
NotifyOfPropertyChange(nameof(HasDataBinding)); NotifyOfPropertyChange(nameof(HasDataBinding));
} }

View File

@ -21,12 +21,11 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
_profileEditorService = profileEditorService; _profileEditorService = profileEditorService;
} }
public async void CopyElement() public void CopyElement()
{ {
Layer layer = Layer.CreateCopy(); Layer layer = Layer.CreateCopy();
_profileEditorService.UpdateSelectedProfile(); _profileEditorService.UpdateSelectedProfile();
// await Task.Delay(200);
_profileEditorService.ChangeSelectedProfileElement(layer); _profileEditorService.ChangeSelectedProfileElement(layer);
} }

View File

@ -16,9 +16,9 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
public abstract class TreeItemViewModel : Conductor<TreeItemViewModel>.Collection.AllActive, IDisposable public abstract class TreeItemViewModel : Conductor<TreeItemViewModel>.Collection.AllActive, IDisposable
{ {
private readonly IDialogService _dialogService; private readonly IDialogService _dialogService;
private readonly ILayerBrushService _layerBrushService;
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private readonly IProfileTreeVmFactory _profileTreeVmFactory; private readonly IProfileTreeVmFactory _profileTreeVmFactory;
private readonly ILayerBrushService _layerBrushService;
private readonly ISurfaceService _surfaceService; private readonly ISurfaceService _surfaceService;
private ProfileElement _profileElement; private ProfileElement _profileElement;
@ -49,11 +49,6 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
public abstract bool SupportsChildren { get; } public abstract bool SupportsChildren { get; }
public void Dispose()
{
Unsubscribe();
}
public List<TreeItemViewModel> GetAllChildren() public List<TreeItemViewModel> GetAllChildren()
{ {
List<TreeItemViewModel> children = new List<TreeItemViewModel>(); List<TreeItemViewModel> children = new List<TreeItemViewModel>();
@ -142,7 +137,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
Layer layer = new Layer(ProfileElement, "New layer"); Layer layer = new Layer(ProfileElement, "New layer");
// Could be null if the default brush got disabled // Could be null if the default brush got disabled
LayerBrushDescriptor? brush = _layerBrushService.GetDefaultLayerBrush(); LayerBrushDescriptor brush = _layerBrushService.GetDefaultLayerBrush();
if (brush != null) if (brush != null)
layer.ChangeLayerBrush(brush); layer.ChangeLayerBrush(brush);
@ -212,7 +207,6 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
List<TreeItemViewModel> newChildren = new List<TreeItemViewModel>(); List<TreeItemViewModel> newChildren = new List<TreeItemViewModel>();
foreach (ProfileElement profileElement in ProfileElement.Children.OrderBy(c => c.Order)) foreach (ProfileElement profileElement in ProfileElement.Children.OrderBy(c => c.Order))
{
if (profileElement is Folder folder) if (profileElement is Folder folder)
{ {
if (Items.FirstOrDefault(p => p is FolderViewModel vm && vm.ProfileElement == folder) == null) if (Items.FirstOrDefault(p => p is FolderViewModel vm && vm.ProfileElement == folder) == null)
@ -223,7 +217,6 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
if (Items.FirstOrDefault(p => p is LayerViewModel vm && vm.ProfileElement == layer) == null) if (Items.FirstOrDefault(p => p is LayerViewModel vm && vm.ProfileElement == layer) == null)
newChildren.Add(_profileTreeVmFactory.LayerViewModel(layer)); newChildren.Add(_profileTreeVmFactory.LayerViewModel(layer));
} }
}
if (!newChildren.Any()) if (!newChildren.Any())
return; return;
@ -262,5 +255,21 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
{ {
UpdateProfileElements(); UpdateProfileElements();
} }
#region IDisposable
protected virtual void Dispose(bool disposing)
{
if (disposing) Unsubscribe();
}
/// <inheritdoc />
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
} }
} }

View File

@ -391,7 +391,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
UpdateCanSelectEditTool(); UpdateCanSelectEditTool();
} }
private void TransformValueChanged(object? sender, LayerPropertyEventArgs e) private void TransformValueChanged(object sender, LayerPropertyEventArgs e)
{ {
if (ActiveToolIndex != 1) if (ActiveToolIndex != 1)
ActivateToolByIndex(1); ActivateToolByIndex(1);

View File

@ -192,7 +192,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
height = layer.Transform.Scale.CurrentValue.Height; height = layer.Transform.Scale.CurrentValue.Height;
break; break;
default: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException(nameof(e));
} }
// Make the sides even if shift is held down // Make the sides even if shift is held down
@ -326,10 +326,14 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
{ {
// Keep the X position static if dragging vertically // Keep the X position static if dragging vertically
if (_draggingVertically) if (_draggingVertically)
{
position.X = _dragStart.X; position.X = _dragStart.X;
}
// Keep the Y position static if dragging horizontally // Keep the Y position static if dragging horizontally
else if (_draggingHorizontally) else if (_draggingHorizontally)
{
position.Y = _dragStart.Y; position.Y = _dragStart.Y;
}
// Snap into place only if the mouse moved atleast a full pixel // Snap into place only if the mouse moved atleast a full pixel
else if (Math.Abs(position.X - _dragStart.X) > 1 || Math.Abs(position.Y - _dragStart.Y) > 1) else if (Math.Abs(position.X - _dragStart.X) > 1 || Math.Abs(position.Y - _dragStart.Y) > 1)
{ {
@ -386,23 +390,23 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
#region Private methods #region Private methods
private SKPoint RoundPoint(SKPoint point, int decimals) private static SKPoint RoundPoint(SKPoint point, int decimals)
{ {
return new SKPoint((float) Math.Round(point.X, decimals, MidpointRounding.AwayFromZero), (float) Math.Round(point.Y, decimals, MidpointRounding.AwayFromZero)); return new SKPoint((float) Math.Round(point.X, decimals, MidpointRounding.AwayFromZero), (float) Math.Round(point.Y, decimals, MidpointRounding.AwayFromZero));
} }
private SKPoint[] UnTransformPoints(SKPoint[] skPoints, Layer layer, SKPoint pivot, bool includeScale) private static SKPoint[] UnTransformPoints(SKPoint[] skPoints, Layer layer, SKPoint pivot, bool includeScale)
{ {
using SKPath counterRotatePath = new SKPath(); using SKPath counterRotatePath = new SKPath();
counterRotatePath.AddPoly(skPoints, false); counterRotatePath.AddPoly(skPoints, false);
counterRotatePath.Transform(SKMatrix.MakeRotationDegrees(layer.Transform.Rotation.CurrentValue * -1, pivot.X, pivot.Y)); counterRotatePath.Transform(SKMatrix.CreateRotationDegrees(layer.Transform.Rotation.CurrentValue * -1, pivot.X, pivot.Y));
if (includeScale) if (includeScale)
counterRotatePath.Transform(SKMatrix.MakeScale(1f / (layer.Transform.Scale.CurrentValue.Width / 100f), 1f / (layer.Transform.Scale.CurrentValue.Height / 100f))); counterRotatePath.Transform(SKMatrix.CreateScale(1f / (layer.Transform.Scale.CurrentValue.Width / 100f), 1f / (layer.Transform.Scale.CurrentValue.Height / 100f)));
return counterRotatePath.Points; return counterRotatePath.Points;
} }
private Point GetRelativePosition(object sender, MouseEventArgs mouseEventArgs) private static Point GetRelativePosition(object sender, MouseEventArgs mouseEventArgs)
{ {
DependencyObject parent = VisualTreeHelper.GetParent((DependencyObject) sender); DependencyObject parent = VisualTreeHelper.GetParent((DependencyObject) sender);
return mouseEventArgs.GetPosition((IInputElement) parent); return mouseEventArgs.GetPosition((IInputElement) parent);
@ -410,7 +414,6 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
private float CalculateAngle(Layer layer, object mouseEventSender, MouseEventArgs mouseEvent) private float CalculateAngle(Layer layer, object mouseEventSender, MouseEventArgs mouseEvent)
{ {
Rect layerBounds = _layerEditorService.GetLayerBounds(layer);
Point start = _layerEditorService.GetLayerAnchorPosition(layer); Point start = _layerEditorService.GetLayerAnchorPosition(layer);
Point arrival = GetRelativePosition(mouseEventSender, mouseEvent); Point arrival = GetRelativePosition(mouseEventSender, mouseEvent);

View File

@ -20,22 +20,22 @@ using Stylet;
namespace Artemis.UI.Screens namespace Artemis.UI.Screens
{ {
public class RootViewModel : Conductor<IScreen> public sealed class RootViewModel : Conductor<IScreen>, IDisposable
{ {
private readonly IRegistrationService _builtInRegistrationService; private readonly IRegistrationService _builtInRegistrationService;
private readonly PluginSetting<ApplicationColorScheme> _colorScheme; private readonly PluginSetting<ApplicationColorScheme> _colorScheme;
private readonly ICoreService _coreService; private readonly ICoreService _coreService;
private readonly IDebugService _debugService; private readonly IDebugService _debugService;
private readonly IEventAggregator _eventAggregator; private readonly IEventAggregator _eventAggregator;
private readonly Timer _frameTimeUpdateTimer;
private readonly ISnackbarMessageQueue _snackbarMessageQueue; private readonly ISnackbarMessageQueue _snackbarMessageQueue;
private readonly ThemeWatcher _themeWatcher; private readonly ThemeWatcher _themeWatcher;
private readonly Timer _frameTimeUpdateTimer;
private readonly PluginSetting<WindowSize> _windowSize; private readonly PluginSetting<WindowSize> _windowSize;
private bool _activeItemReady; private bool _activeItemReady;
private string _frameTime;
private bool _lostFocus; private bool _lostFocus;
private ISnackbarMessageQueue _mainMessageQueue; private ISnackbarMessageQueue _mainMessageQueue;
private string _windowTitle; private string _windowTitle;
private string _frameTime;
public RootViewModel( public RootViewModel(
IEventAggregator eventAggregator, IEventAggregator eventAggregator,
@ -64,7 +64,7 @@ namespace Artemis.UI.Screens
ActiveItem = SidebarViewModel.SelectedItem; ActiveItem = SidebarViewModel.SelectedItem;
ActiveItemReady = true; ActiveItemReady = true;
AssemblyInformationalVersionAttribute? versionAttribute = typeof(RootViewModel).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>(); AssemblyInformationalVersionAttribute versionAttribute = typeof(RootViewModel).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
WindowTitle = $"Artemis {versionAttribute?.InformationalVersion}"; WindowTitle = $"Artemis {versionAttribute?.InformationalVersion}";
} }
@ -141,19 +141,18 @@ namespace Artemis.UI.Screens
private void UpdateFrameTime() private void UpdateFrameTime()
{ {
FrameTime = $"Frame time: {_coreService.FrameTime.TotalMilliseconds:F2} ms"; FrameTime = $"Frame time: {_coreService.FrameTime.TotalMilliseconds:F2} ms";
} }
private void SidebarViewModelOnPropertyChanged(object sender, PropertyChangedEventArgs e) private void SidebarViewModelOnPropertyChanged(object sender, PropertyChangedEventArgs e)
{ {
if (e.PropertyName == nameof(SidebarViewModel.SelectedItem) && ActiveItem != SidebarViewModel.SelectedItem) if (e.PropertyName == nameof(SidebarViewModel.SelectedItem) && ActiveItem != SidebarViewModel.SelectedItem)
{ {
SidebarViewModel.IsSidebarOpen = false; SidebarViewModel.IsSidebarOpen = false;
// Don't do a fade when selecting a module because the editor is so bulky the animation slows things down // Don't do a fade when selecting a module because the editor is so bulky the animation slows things down
if (!(SidebarViewModel.SelectedItem is ModuleRootViewModel)) if (!(SidebarViewModel.SelectedItem is ModuleRootViewModel))
ActiveItemReady = false; ActiveItemReady = false;
// Allow the menu to close, it's slower but feels more responsive, funny how that works right // Allow the menu to close, it's slower but feels more responsive, funny how that works right
Execute.PostToUIThreadAsync(async () => Execute.PostToUIThreadAsync(async () =>
{ {
@ -209,13 +208,25 @@ namespace Artemis.UI.Screens
ApplyColorSchemeSetting(); ApplyColorSchemeSetting();
} }
#region IDisposable
/// <inheritdoc />
public void Dispose()
{
_frameTimeUpdateTimer?.Dispose();
}
#endregion
#region Overrides of Screen #region Overrides of Screen
protected override void OnViewLoaded() protected override void OnViewLoaded()
{ {
MaterialWindow window = (MaterialWindow) View; MaterialWindow window = (MaterialWindow) View;
if (_windowSize.Value != null) if (_windowSize.Value != null)
{
_windowSize.Value.ApplyToWindow(window); _windowSize.Value.ApplyToWindow(window);
}
else else
{ {
_windowSize.Value = new WindowSize(); _windowSize.Value = new WindowSize();
@ -269,7 +280,7 @@ namespace Artemis.UI.Screens
protected override void OnClose() protected override void OnClose()
{ {
SidebarViewModel.Dispose(); SidebarViewModel.Dispose();
// Lets force the GC to run after closing the window so it is obvious to users watching task manager // 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. // that closing the UI will decrease the memory footprint of the application.
Task.Run(async () => Task.Run(async () =>

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System;
using System.Linq; using System.Linq;
using System.Timers; using System.Timers;
using Artemis.Core; using Artemis.Core;
@ -10,14 +10,14 @@ using Stylet;
namespace Artemis.UI.Screens.Settings.Debug.Tabs namespace Artemis.UI.Screens.Settings.Debug.Tabs
{ {
public class DataModelDebugViewModel : Screen public sealed class DataModelDebugViewModel : Screen, IDisposable
{ {
private readonly IDataModelUIService _dataModelUIService; private readonly IDataModelUIService _dataModelUIService;
private readonly IPluginManagementService _pluginManagementService; private readonly IPluginManagementService _pluginManagementService;
private readonly Timer _updateTimer; private readonly Timer _updateTimer;
private bool _isModuleFilterEnabled; private bool _isModuleFilterEnabled;
private DataModelPropertiesViewModel _mainDataModel; private DataModelPropertiesViewModel _mainDataModel;
private List<Module> _modules;
private string _propertySearch; private string _propertySearch;
private Module _selectedModule; private Module _selectedModule;
@ -69,6 +69,17 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
} }
} }
#region Overrides of Screen
/// <inheritdoc />
protected override void OnClose()
{
_updateTimer.Dispose();
base.OnClose();
}
#endregion
protected override void OnActivate() protected override void OnActivate()
{ {
GetDataModel(); GetDataModel();
@ -126,5 +137,15 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
else if (!SelectedModule.IsEnabled) else if (!SelectedModule.IsEnabled)
SelectedModule = null; SelectedModule = null;
} }
#region IDisposable
/// <inheritdoc />
public void Dispose()
{
_updateTimer?.Dispose();
}
#endregion
} }
} }

View File

@ -11,7 +11,6 @@ namespace Artemis.UI.Screens.Settings.Tabs.Devices
{ {
private readonly ISettingsVmFactory _settingsVmFactory; private readonly ISettingsVmFactory _settingsVmFactory;
private readonly ISurfaceService _surfaceService; private readonly ISurfaceService _surfaceService;
private BindableCollection<DeviceSettingsViewModel> _deviceSettingsViewModels;
public DeviceSettingsTabViewModel(ISurfaceService surfaceService, ISettingsVmFactory settingsVmFactory) public DeviceSettingsTabViewModel(ISurfaceService surfaceService, ISettingsVmFactory settingsVmFactory)
{ {

View File

@ -125,34 +125,27 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
private PackIconKind GetIconKind() private PackIconKind GetIconKind()
{ {
switch (Feature) return Feature switch
{ {
case BaseDataModelExpansion _: BaseDataModelExpansion => PackIconKind.TableAdd,
return PackIconKind.TableAdd; DeviceProvider => PackIconKind.Devices,
case DeviceProvider _: ProfileModule => PackIconKind.VectorRectangle,
return PackIconKind.Devices; Module => PackIconKind.GearBox,
case ProfileModule _: LayerBrushProvider => PackIconKind.Brush,
return PackIconKind.VectorRectangle; LayerEffectProvider => PackIconKind.AutoAwesome,
case Module _: _ => PackIconKind.Plugin
return PackIconKind.GearBox; };
case LayerBrushProvider _:
return PackIconKind.Brush;
case LayerEffectProvider _:
return PackIconKind.AutoAwesome;
}
return PackIconKind.Plugin;
} }
#region Event handlers #region Event handlers
private void OnFeatureEnabling(object? sender, PluginFeatureEventArgs e) private void OnFeatureEnabling(object sender, PluginFeatureEventArgs e)
{ {
if (e.PluginFeature != Feature) return; if (e.PluginFeature != Feature) return;
Enabling = true; Enabling = true;
} }
private void OnFeatureEnableStopped(object? sender, PluginFeatureEventArgs e) private void OnFeatureEnableStopped(object sender, PluginFeatureEventArgs e)
{ {
if (e.PluginFeature != Feature) return; if (e.PluginFeature != Feature) return;
Enabling = false; Enabling = false;

View File

@ -146,14 +146,14 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
NotifyOfPropertyChange(nameof(CanOpenSettings)); NotifyOfPropertyChange(nameof(CanOpenSettings));
} }
private void PluginOnFeatureRemoved(object? sender, PluginFeatureEventArgs e) private void PluginOnFeatureRemoved(object sender, PluginFeatureEventArgs e)
{ {
PluginFeatureViewModel viewModel = Items.FirstOrDefault(i => i.Feature == e.PluginFeature); PluginFeatureViewModel viewModel = Items.FirstOrDefault(i => i.Feature == e.PluginFeature);
if (viewModel != null) if (viewModel != null)
Items.Remove(viewModel); Items.Remove(viewModel);
} }
private void PluginOnFeatureAdded(object? sender, PluginFeatureEventArgs e) private void PluginOnFeatureAdded(object sender, PluginFeatureEventArgs e)
{ {
Items.Add(_settingsVmFactory.CreatePluginFeatureViewModel(e.PluginFeature)); Items.Add(_settingsVmFactory.CreatePluginFeatureViewModel(e.PluginFeature));
} }

View File

@ -22,7 +22,7 @@ using Stylet;
namespace Artemis.UI.Screens.Sidebar namespace Artemis.UI.Screens.Sidebar
{ {
public class SidebarViewModel : PropertyChangedBase, IHandle<RequestSelectSidebarItemEvent>, IDisposable public sealed class SidebarViewModel : PropertyChangedBase, IHandle<RequestSelectSidebarItemEvent>, IDisposable
{ {
private readonly Timer _activeModulesUpdateTimer; private readonly Timer _activeModulesUpdateTimer;
private readonly IKernel _kernel; private readonly IKernel _kernel;
@ -196,16 +196,9 @@ namespace Artemis.UI.Screens.Sidebar
#region IDisposable #region IDisposable
protected virtual void Dispose(bool disposing)
{
if (disposing)
_activeModulesUpdateTimer?.Dispose();
}
public void Dispose() public void Dispose()
{ {
Dispose(true); _activeModulesUpdateTimer?.Dispose();
GC.SuppressFinalize(this);
} }
#endregion #endregion

View File

@ -81,12 +81,12 @@ namespace Artemis.UI.Screens.Splash
Status = "Enabling plugin: " + args.Plugin.Info.Name; Status = "Enabling plugin: " + args.Plugin.Info.Name;
} }
private void PluginManagementServiceOnPluginFeatureEnabling(object? sender, PluginFeatureEventArgs e) private void PluginManagementServiceOnPluginFeatureEnabling(object sender, PluginFeatureEventArgs e)
{ {
Status = "Enabling: " + e.PluginFeature.GetType().Name.Humanize(); Status = "Enabling: " + e.PluginFeature.GetType().Name.Humanize();
} }
private void PluginManagementServiceOnPluginFeatureEnabled(object? sender, PluginFeatureEventArgs e) private void PluginManagementServiceOnPluginFeatureEnabled(object sender, PluginFeatureEventArgs e)
{ {
Status = "Initializing UI"; Status = "Initializing UI";
} }

View File

@ -4,7 +4,6 @@ using System.Windows.Media;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Services.Interfaces; using Artemis.UI.Services.Interfaces;
using Artemis.UI.Shared.Services;
using SkiaSharp; using SkiaSharp;
using SkiaSharp.Views.WPF; using SkiaSharp.Views.WPF;
@ -14,7 +13,7 @@ namespace Artemis.UI.Services
{ {
private readonly ISettingsService _settingsService; private readonly ISettingsService _settingsService;
public LayerEditorService(ISettingsService settingsService, IProfileEditorService profileEditorService) public LayerEditorService(ISettingsService settingsService)
{ {
_settingsService = settingsService; _settingsService = settingsService;
} }
@ -93,11 +92,11 @@ namespace Artemis.UI.Services
SKPath path = new SKPath(); SKPath path = new SKPath();
path.AddRect(layerBounds); path.AddRect(layerBounds);
if (includeTranslation) if (includeTranslation)
path.Transform(SKMatrix.MakeTranslation(x, y)); path.Transform(SKMatrix.CreateTranslation(x, y));
if (includeScale) if (includeScale)
path.Transform(SKMatrix.MakeScale(layer.Transform.Scale.CurrentValue.Width / 100f, layer.Transform.Scale.CurrentValue.Height / 100f, anchorPosition.X, anchorPosition.Y)); path.Transform(SKMatrix.CreateScale(layer.Transform.Scale.CurrentValue.Width / 100f, layer.Transform.Scale.CurrentValue.Height / 100f, anchorPosition.X, anchorPosition.Y));
if (includeRotation) if (includeRotation)
path.Transform(SKMatrix.MakeRotationDegrees(layer.Transform.Rotation.CurrentValue, anchorPosition.X, anchorPosition.Y)); path.Transform(SKMatrix.CreateRotationDegrees(layer.Transform.Rotation.CurrentValue, anchorPosition.X, anchorPosition.Y));
return path; return path;
} }
@ -107,12 +106,10 @@ namespace Artemis.UI.Services
{ {
double renderScale = _settingsService.GetSetting("Core.RenderScale", 0.5).Value; double renderScale = _settingsService.GetSetting("Core.RenderScale", 0.5).Value;
if (absolute) if (absolute)
{
return new SKPoint( return new SKPoint(
100f / layer.Bounds.Width * ((float) (point.X * renderScale) - layer.Bounds.Left) / 100f, 100f / layer.Bounds.Width * ((float) (point.X * renderScale) - layer.Bounds.Left) / 100f,
100f / layer.Bounds.Height * ((float) (point.Y * renderScale) - layer.Bounds.Top) / 100f 100f / layer.Bounds.Height * ((float) (point.Y * renderScale) - layer.Bounds.Top) / 100f
); );
}
return new SKPoint( return new SKPoint(
100f / layer.Bounds.Width * (float) (point.X * renderScale) / 100f, 100f / layer.Bounds.Width * (float) (point.X * renderScale) / 100f,

View File

@ -74,7 +74,7 @@ namespace Artemis.UI.Services
_registeredBuiltInPropertyEditors = true; _registeredBuiltInPropertyEditors = true;
} }
private void PluginServiceOnPluginLoaded(object? sender, PluginEventArgs e) private void PluginServiceOnPluginLoaded(object sender, PluginEventArgs e)
{ {
e.Plugin.Kernel.Load(new[] {new PluginUIModule(e.Plugin)}); e.Plugin.Kernel.Load(new[] {new PluginUIModule(e.Plugin)});
} }

View File

@ -1,6 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using FluentValidation; using FluentValidation;
using Stylet; using Stylet;
@ -26,7 +25,7 @@ namespace Artemis.UI.Stylet
{ {
// If someone's calling us synchronously, and ValidationAsync does not complete synchronously, // If someone's calling us synchronously, and ValidationAsync does not complete synchronously,
// we'll deadlock unless we continue on another thread. // we'll deadlock unless we continue on another thread.
return (await _validator.ValidateAsync(_subject, CancellationToken.None, propertyName).ConfigureAwait(false)) return (await _validator.ValidateAsync(_subject, options => options.IncludeProperties(propertyName)).ConfigureAwait(false))
.Errors.Select(x => x.ErrorMessage); .Errors.Select(x => x.ErrorMessage);
} }