Profile editor - Redesigned adding/removing timeline segments

General module - Added some basic window information to the data model
Profiles - Don't render when opacity is 0
Profile editor - Render non-selected layers in their main segment
Profile editor - Condition editor fixes
This commit is contained in:
SpoinkyNL committed 2020-07-24 23:04:21 +02:00
1 parent 732ee2f429
commit 64a1010100
39 files changed
+831 -261

No files matched your search

@@ -167,7 +167,7 @@ namespace Artemis.UI.Shared.Controls
private void Input_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
var seperator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
var regex = new Regex("^[" + seperator + "][0-9]+$|^[0-9]*[" + seperator + "]{0,1}[0-9]*$");
var regex = new Regex("^[" + seperator + "][-|0-9]+$|^-?[0-9]*[" + seperator + "]{0,1}[0-9]*$");
e.Handled = !regex.IsMatch(e.Text);
}
@@ -58,7 +58,7 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
foreach (var propertyInfo in ListType.GetProperties())
{
var child = CreateChild(dataModelVisualizationService, propertyInfo);
var child = CreateChild(dataModelVisualizationService, propertyInfo, GetChildDepth());
if (child != null)
Children.Add(child);
}
@@ -35,6 +35,9 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
public override void Update(IDataModelVisualizationService dataModelVisualizationService)
{
if (Parent != null && !Parent.IsVisualizationExpanded)
return;
List = GetCurrentValue() as IList;
if (List == null)
return;
@@ -10,10 +10,16 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
internal DataModelPropertiesViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, PropertyInfo propertyInfo) : base(dataModel, parent, propertyInfo)
{
}
public override void Update(IDataModelVisualizationService dataModelVisualizationService)
{
// Always populate properties
PopulateProperties(dataModelVisualizationService);
// Only update children if the parent is expanded
if (Parent != null && !Parent.IsVisualizationExpanded && !Parent.IsRootViewModel)
return;
foreach (var dataModelVisualizationViewModel in Children)
dataModelVisualizationViewModel.Update(dataModelVisualizationService);
}
@@ -22,7 +28,7 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
{
return Parent.IsRootViewModel ? DataModel : base.GetCurrentValue();
}
private void PopulateProperties(IDataModelVisualizationService dataModelVisualizationService)
{
if (Children.Any())
@@ -31,10 +37,15 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
var modelType = Parent.IsRootViewModel ? DataModel.GetType() : PropertyInfo.PropertyType;
foreach (var propertyInfo in modelType.GetProperties())
{
var child = CreateChild(dataModelVisualizationService, propertyInfo);
var child = CreateChild(dataModelVisualizationService, propertyInfo, GetChildDepth());
if (child != null)
Children.Add(child);
}
}
protected int GetChildDepth()
{
return PropertyDescription != null && !PropertyDescription.ResetsDepth ? Depth + 1 : 1;
}
}
}
@@ -49,6 +49,9 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
public override void Update(IDataModelVisualizationService dataModelVisualizationService)
{
if (Parent != null && !Parent.IsVisualizationExpanded && !Parent.IsRootViewModel)
return;
if (DisplayViewModel == null && dataModelVisualizationService.RegisteredDataModelDisplays.Any(d => d.SupportedType == PropertyInfo.PropertyType))
dataModelVisualizationService.GetDataModelDisplayViewModel(PropertyInfo.PropertyType);
@@ -15,9 +15,11 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
{
public abstract class DataModelVisualizationViewModel : PropertyChangedBase
{
private const int MaxDepth = 4;
private BindableCollection<DataModelVisualizationViewModel> _children;
private DataModel _dataModel;
private bool _isMatchingFilteredTypes;
private bool _isVisualizationExpanded;
private DataModelVisualizationViewModel _parent;
private DataModelPropertyAttribute _propertyDescription;
private PropertyInfo _propertyInfo;
@@ -37,6 +39,7 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
}
public bool IsRootViewModel { get; }
public int Depth { get; set; }
public DataModel DataModel
{
@@ -74,6 +77,16 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
set => SetAndNotify(ref _isMatchingFilteredTypes, value);
}
public bool IsVisualizationExpanded
{
get => _isVisualizationExpanded;
set
{
if (!SetAndNotify(ref _isVisualizationExpanded, value)) return;
RequestUpdate();
}
}
public string PropertyPath
{
get
@@ -104,11 +117,26 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
}
}
/// <summary>
/// Updates the datamodel and if in an parent, any children
/// </summary>
/// <param name="dataModelVisualizationService"></param>
public abstract void Update(IDataModelVisualizationService dataModelVisualizationService);
public virtual object GetCurrentValue()
{
return Parent == null ? null : PropertyInfo.GetValue(Parent.GetCurrentValue());
try
{
if (PropertyInfo.GetGetMethod() == null)
return null;
return Parent == null ? null : PropertyInfo.GetValue(Parent.GetCurrentValue());
}
catch (Exception)
{
// ignored, who knows what kind of shit can go wrong here...
return null;
}
}
public void ApplyTypeFilter(bool looseMatch, params Type[] filteredTypes)
@@ -157,6 +185,7 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
{
if (predicate.LeftDataModel == null || predicate.LeftPropertyPath == null)
return null;
return GetChildByPath(predicate.LeftDataModel.PluginInfo.Guid, predicate.LeftPropertyPath);
}
@@ -167,6 +196,14 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
public DataModelVisualizationViewModel GetChildByPath(Guid dataModelGuid, string propertyPath)
{
// Ensure children are populated by requesting an update
if (!IsVisualizationExpanded)
{
IsVisualizationExpanded = true;
RequestUpdate();
IsVisualizationExpanded = false;
}
var path = propertyPath.Split(".");
var currentPart = path.First();
@@ -187,8 +224,10 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
}
}
protected DataModelVisualizationViewModel CreateChild(IDataModelVisualizationService dataModelVisualizationService, PropertyInfo propertyInfo)
protected DataModelVisualizationViewModel CreateChild(IDataModelVisualizationService dataModelVisualizationService, PropertyInfo propertyInfo, int depth)
{
if (depth > MaxDepth)
return null;
// Skip properties decorated with DataModelIgnore
if (Attribute.IsDefined(propertyInfo, typeof(DataModelIgnoreAttribute)))
return null;
@@ -196,19 +235,36 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
// If a display VM was found, prefer to use that in any case
var typeViewModel = dataModelVisualizationService.GetDataModelDisplayViewModel(propertyInfo.PropertyType);
if (typeViewModel != null)
return new DataModelPropertyViewModel(DataModel, this, propertyInfo) {DisplayViewModel = typeViewModel};
return new DataModelPropertyViewModel(DataModel, this, propertyInfo) {DisplayViewModel = typeViewModel, Depth = depth};
// For primitives, create a property view model, it may be null that is fine
if (propertyInfo.PropertyType.IsPrimitive || propertyInfo.PropertyType == typeof(string))
return new DataModelPropertyViewModel(DataModel, this, propertyInfo);
return new DataModelPropertyViewModel(DataModel, this, propertyInfo) {Depth = depth};
if (typeof(IList).IsAssignableFrom(propertyInfo.PropertyType))
return new DataModelListViewModel(DataModel, this, propertyInfo);
return new DataModelListViewModel(DataModel, this, propertyInfo) {Depth = depth};
// For other value types create a child view model
if (propertyInfo.PropertyType.IsClass || propertyInfo.PropertyType.IsStruct())
return new DataModelPropertiesViewModel(DataModel, this, propertyInfo);
return new DataModelPropertiesViewModel(DataModel, this, propertyInfo) {Depth = depth};
return null;
}
#region Events
public event EventHandler UpdateRequested;
protected virtual void OnUpdateRequested()
{
UpdateRequested?.Invoke(this, EventArgs.Empty);
}
#endregion
private void RequestUpdate()
{
Parent?.RequestUpdate();
OnUpdateRequested();
}
private void GetDescription()
{
// If this is the first child of a root view model, use the data model description
@@ -218,7 +274,7 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared
else if (PropertyInfo != null)
{
PropertyDescription = (DataModelPropertyAttribute) Attribute.GetCustomAttribute(PropertyInfo, typeof(DataModelPropertyAttribute)) ??
new DataModelPropertyAttribute {Name = PropertyInfo.Name.Humanize()};
new DataModelPropertyAttribute {Name = PropertyInfo.Name.Humanize(), ResetsDepth = false};
}
else
throw new ArtemisSharedUIException("Failed to get property description because plugin info is null but the parent has a datamodel");
@@ -42,6 +42,7 @@ namespace Artemis.UI.Shared.Services
// Update to populate children
viewModel.Update(this);
viewModel.UpdateRequested += (sender, args) => viewModel.Update(this);
return viewModel;
}
@@ -56,6 +57,7 @@ namespace Artemis.UI.Shared.Services
// Update to populate children
viewModel.Update(this);
viewModel.UpdateRequested += (sender, args) => viewModel.Update(this);
return viewModel;
}
@@ -185,6 +187,7 @@ namespace Artemis.UI.Shared.Services
if (initialValue == null)
initialValue = Activator.CreateInstance(registration.SupportedType);
}
// This assumes the type can be converted, that has been checked when the VM was created
if (initialValue != null && initialValue.GetType() != registration.SupportedType)
initialValue = Convert.ChangeType(initialValue, registration.SupportedType);
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Artemis.Core.Models.Profile;
using Artemis.Core.Models.Profile.LayerProperties;
using Artemis.Core.Plugins.Abstract;
using Artemis.Core.Plugins.Models;
using Artemis.UI.Shared.Events;
@@ -53,7 +54,7 @@ namespace Artemis.UI.Shared.Services.Interfaces
/// Occurs when the current editor time is changed
/// </summary>
event EventHandler CurrentTimeChanged;
/// <summary>
/// Occurs when the pixels per second (zoom level) is changed
/// </summary>
@@ -76,11 +77,16 @@ namespace Artemis.UI.Shared.Services.Interfaces
void RemovePropertyInput(PropertyInputRegistration registration);
/// <summary>
/// Snaps the given time to the closest relevant element in the timeline, this can be the cursor, a keyframe or a segment end.
/// Snaps the given time to the closest relevant element in the timeline, this can be the cursor, a keyframe or a
/// segment end.
/// </summary>
/// <param name="time"></param>
/// <param name="tolerance">How close the time must be to snap</param>
/// <param name="snapToSegments">Enable snapping to timeline segments</param>
/// <param name="snapToCurrentTime">Enable snapping to the current time of the editor</param>
/// <param name="snapToKeyframes">Enable snapping to visible keyframes</param>
/// <param name="excludedKeyframe">A keyframe to exclude during keyframe snapping</param>
/// <returns></returns>
TimeSpan SnapToTimeline(TimeSpan time, TimeSpan tolerance, bool snapToSegments, bool snapToCurrentTime, bool snapToKeyframes);
TimeSpan SnapToTimeline(TimeSpan time, TimeSpan tolerance, bool snapToSegments, bool snapToCurrentTime, bool snapToKeyframes, BaseLayerPropertyKeyframe excludedKeyframe = null);
}
}
@@ -113,20 +113,11 @@ namespace Artemis.UI.Shared.Services
if (SelectedProfile == null)
return;
var delta = CurrentTime - _lastUpdateTime;
foreach (var folder in SelectedProfile.GetAllFolders())
{
foreach (var baseLayerEffect in folder.LayerEffects)
baseLayerEffect.Update(delta.TotalSeconds);
}
foreach (var layer in SelectedProfile.GetAllLayers())
{
layer.OverrideProgress(CurrentTime);
layer.LayerBrush?.Update(delta.TotalSeconds);
foreach (var baseLayerEffect in layer.LayerEffects)
baseLayerEffect.Update(delta.TotalSeconds);
}
// Stick to the main segment for any element that is not currently selected
foreach (var folder in SelectedProfile.GetAllFolders())
folder.OverrideProgress(CurrentTime, folder != SelectedProfileElement);
foreach (var layer in SelectedProfile.GetAllLayers())
layer.OverrideProgress(CurrentTime, layer != SelectedProfileElement);
_lastUpdateTime = CurrentTime;
OnProfilePreviewUpdated();
@@ -205,7 +196,7 @@ namespace Artemis.UI.Shared.Services
}
}
public TimeSpan SnapToTimeline(TimeSpan time, TimeSpan tolerance, bool snapToSegments, bool snapToCurrentTime, bool snapToKeyframes)
public TimeSpan SnapToTimeline(TimeSpan time, TimeSpan tolerance, bool snapToSegments, bool snapToCurrentTime, bool snapToKeyframes, BaseLayerPropertyKeyframe excludedKeyframe = null)
{
if (snapToSegments)
{