diff --git a/src/Artemis.Core/Models/Profile/Folder.cs b/src/Artemis.Core/Models/Profile/Folder.cs
index a1f8400e8..81ec38993 100644
--- a/src/Artemis.Core/Models/Profile/Folder.cs
+++ b/src/Artemis.Core/Models/Profile/Folder.cs
@@ -14,6 +14,8 @@ namespace Artemis.Core
///
public sealed class Folder : RenderProfileElement
{
+ private bool _isExpanded;
+
///
/// Creates a new instance of the class and adds itself to the child collection of the provided
///
@@ -46,6 +48,7 @@ namespace Artemis.Core
Profile = profile;
Parent = parent;
Name = folderEntity.Name;
+ IsExpanded = folderEntity.IsExpanded;
Suspended = folderEntity.Suspended;
Order = folderEntity.Order;
@@ -57,6 +60,15 @@ namespace Artemis.Core
///
public bool IsRootFolder => Parent == Profile;
+ ///
+ /// Gets or sets a boolean indicating whether this folder is expanded
+ ///
+ public bool IsExpanded
+ {
+ get => _isExpanded;
+ set => SetAndNotify(ref _isExpanded, value);
+ }
+
///
/// Gets the folder entity this folder uses for persistent storage
///
@@ -72,8 +84,10 @@ namespace Artemis.Core
{
List result = new();
foreach (BaseLayerEffect layerEffect in LayerEffects)
+ {
if (layerEffect.BaseProperties != null)
result.AddRange(layerEffect.BaseProperties.GetAllLayerProperties());
+ }
return result;
}
@@ -151,27 +165,6 @@ namespace Artemis.Core
return $"[Folder] {nameof(Name)}: {Name}, {nameof(Order)}: {Order}";
}
- internal void CalculateRenderProperties()
- {
- if (Disposed)
- throw new ObjectDisposedException("Folder");
-
- SKPath path = new() {FillType = SKPathFillType.Winding};
- foreach (ProfileElement child in Children)
- {
- if (child is RenderProfileElement effectChild && effectChild.Path != null)
- path.AddPath(effectChild.Path);
- }
-
- Path = path;
-
- // Folder render properties are based on child paths and thus require an update
- if (Parent is Folder folder)
- folder.CalculateRenderProperties();
-
- OnRenderPropertiesUpdated();
- }
-
#region Rendering
///
@@ -209,7 +202,7 @@ namespace Artemis.Core
canvas.SaveLayer(layerPaint);
canvas.Translate(Bounds.Left - basePosition.X, Bounds.Top - basePosition.Y);
-
+
// Iterate the children in reverse because the first layer must be rendered last to end up on top
for (int index = Children.Count - 1; index > -1; index--)
Children[index].Render(canvas, new SKPointI(Bounds.Left, Bounds.Top));
@@ -229,57 +222,6 @@ namespace Artemis.Core
#endregion
- ///
- protected override void Dispose(bool disposing)
- {
- Disposed = true;
-
- Disable();
- foreach (ProfileElement profileElement in Children)
- profileElement.Dispose();
-
- base.Dispose(disposing);
- }
-
- internal override void Load()
- {
- ExpandedPropertyGroups.AddRange(FolderEntity.ExpandedPropertyGroups);
- Reset();
-
- // Load child folders
- foreach (FolderEntity childFolder in Profile.ProfileEntity.Folders.Where(f => f.ParentId == EntityId))
- ChildrenList.Add(new Folder(Profile, this, childFolder));
- // Load child layers
- foreach (LayerEntity childLayer in Profile.ProfileEntity.Layers.Where(f => f.ParentId == EntityId))
- ChildrenList.Add(new Layer(Profile, this, childLayer));
-
- // Ensure order integrity, should be unnecessary but no one is perfect specially me
- ChildrenList = ChildrenList.OrderBy(c => c.Order).ToList();
- for (int index = 0; index < ChildrenList.Count; index++)
- ChildrenList[index].Order = index + 1;
-
- LoadRenderElement();
- }
-
- internal override void Save()
- {
- if (Disposed)
- throw new ObjectDisposedException("Folder");
-
- FolderEntity.Id = EntityId;
- FolderEntity.ParentId = Parent?.EntityId ?? new Guid();
-
- FolderEntity.Order = Order;
- FolderEntity.Name = Name;
- FolderEntity.Suspended = Suspended;
-
- FolderEntity.ProfileId = Profile.EntityId;
- FolderEntity.ExpandedPropertyGroups.Clear();
- FolderEntity.ExpandedPropertyGroups.AddRange(ExpandedPropertyGroups);
-
- SaveRenderElement();
- }
-
///
public override void Enable()
{
@@ -320,18 +262,87 @@ namespace Artemis.Core
Enabled = false;
}
- #region Events
-
///
/// Occurs when a property affecting the rendering properties of this folder has been updated
///
public event EventHandler? RenderPropertiesUpdated;
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ Disposed = true;
+
+ Disable();
+ foreach (ProfileElement profileElement in Children)
+ profileElement.Dispose();
+
+ base.Dispose(disposing);
+ }
+
+ internal void CalculateRenderProperties()
+ {
+ if (Disposed)
+ throw new ObjectDisposedException("Folder");
+
+ SKPath path = new() {FillType = SKPathFillType.Winding};
+ foreach (ProfileElement child in Children)
+ {
+ if (child is RenderProfileElement effectChild && effectChild.Path != null)
+ path.AddPath(effectChild.Path);
+ }
+
+ Path = path;
+
+ // Folder render properties are based on child paths and thus require an update
+ if (Parent is Folder folder)
+ folder.CalculateRenderProperties();
+
+ OnRenderPropertiesUpdated();
+ }
+
+ internal override void Load()
+ {
+ ExpandedPropertyGroups.AddRange(FolderEntity.ExpandedPropertyGroups);
+ Reset();
+
+ // Load child folders
+ foreach (FolderEntity childFolder in Profile.ProfileEntity.Folders.Where(f => f.ParentId == EntityId))
+ ChildrenList.Add(new Folder(Profile, this, childFolder));
+ // Load child layers
+ foreach (LayerEntity childLayer in Profile.ProfileEntity.Layers.Where(f => f.ParentId == EntityId))
+ ChildrenList.Add(new Layer(Profile, this, childLayer));
+
+ // Ensure order integrity, should be unnecessary but no one is perfect specially me
+ ChildrenList = ChildrenList.OrderBy(c => c.Order).ToList();
+ for (int index = 0; index < ChildrenList.Count; index++)
+ ChildrenList[index].Order = index + 1;
+
+ LoadRenderElement();
+ }
+
+ internal override void Save()
+ {
+ if (Disposed)
+ throw new ObjectDisposedException("Folder");
+
+ FolderEntity.Id = EntityId;
+ FolderEntity.ParentId = Parent?.EntityId ?? new Guid();
+
+ FolderEntity.Order = Order;
+ FolderEntity.Name = Name;
+ FolderEntity.IsExpanded = IsExpanded;
+ FolderEntity.Suspended = Suspended;
+
+ FolderEntity.ProfileId = Profile.EntityId;
+ FolderEntity.ExpandedPropertyGroups.Clear();
+ FolderEntity.ExpandedPropertyGroups.AddRange(ExpandedPropertyGroups);
+
+ SaveRenderElement();
+ }
+
private void OnRenderPropertiesUpdated()
{
RenderPropertiesUpdated?.Invoke(this, EventArgs.Empty);
}
-
- #endregion
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/Profile.cs b/src/Artemis.Core/Models/Profile/Profile.cs
index 75ec5d66b..e2870dec1 100644
--- a/src/Artemis.Core/Models/Profile/Profile.cs
+++ b/src/Artemis.Core/Models/Profile/Profile.cs
@@ -14,6 +14,7 @@ namespace Artemis.Core
{
private readonly object _lock = new();
private bool _isFreshImport;
+ private ProfileElement? _lastSelectedProfileElement;
internal Profile(ProfileConfiguration configuration, ProfileEntity profileEntity) : base(null!)
{
@@ -60,6 +61,15 @@ namespace Artemis.Core
set => SetAndNotify(ref _isFreshImport, value);
}
+ ///
+ /// Gets or sets the last selected profile element of this profile
+ ///
+ public ProfileElement? LastSelectedProfileElement
+ {
+ get => _lastSelectedProfileElement;
+ set => SetAndNotify(ref _lastSelectedProfileElement, value);
+ }
+
///
/// Gets the profile entity this profile uses for persistent storage
///
@@ -187,6 +197,15 @@ namespace Artemis.Core
}
}
+ if (ProfileEntity.LastSelectedProfileElement != Guid.Empty)
+ {
+ LastSelectedProfileElement = GetAllFolders().FirstOrDefault(f => f.EntityId == ProfileEntity.LastSelectedProfileElement);
+ if (LastSelectedProfileElement == null)
+ LastSelectedProfileElement = GetAllLayers().FirstOrDefault(f => f.EntityId == ProfileEntity.LastSelectedProfileElement);
+ }
+ else
+ LastSelectedProfileElement = null;
+
foreach (ScriptConfiguration scriptConfiguration in ScriptConfigurations)
scriptConfiguration.Script?.Dispose();
ScriptConfigurations.Clear();
@@ -201,6 +220,7 @@ namespace Artemis.Core
ProfileEntity.Id = EntityId;
ProfileEntity.Name = Configuration.Name;
ProfileEntity.IsFreshImport = IsFreshImport;
+ ProfileEntity.LastSelectedProfileElement = LastSelectedProfileElement?.EntityId ?? Guid.Empty;
foreach (ProfileElement profileElement in Children)
profileElement.Save();
diff --git a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs
index f8d8a6d47..bd4b4994a 100644
--- a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs
+++ b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs
@@ -397,7 +397,7 @@ namespace Artemis.Core
if (conditionMet && !DisplayConditionMet && Timeline.IsFinished)
Timeline.JumpToStart();
// If regular conditions are no longer met, jump to the end segment if stop mode requires it
- if (!conditionMet && DisplayConditionMet && Timeline.StopMode == TimelineStopMode.SkipToEnd)
+ if (!conditionMet && Timeline.StopMode == TimelineStopMode.SkipToEnd)
Timeline.JumpToEndSegment();
}
else if (conditionMet)
diff --git a/src/Artemis.Core/Models/ProfileConfiguration/ProfileConfiguration.cs b/src/Artemis.Core/Models/ProfileConfiguration/ProfileConfiguration.cs
index f4103388e..21e1a3157 100644
--- a/src/Artemis.Core/Models/ProfileConfiguration/ProfileConfiguration.cs
+++ b/src/Artemis.Core/Models/ProfileConfiguration/ProfileConfiguration.cs
@@ -179,7 +179,8 @@ namespace Artemis.Core
{
if (_disposed)
throw new ObjectDisposedException("ProfileConfiguration");
-
+ if (IsBeingEdited)
+ return true;
if (Category.IsSuspended || IsSuspended || IsMissingModule)
return false;
diff --git a/src/Artemis.Core/Services/Storage/ProfileService.cs b/src/Artemis.Core/Services/Storage/ProfileService.cs
index f026abccb..5cdf00f06 100644
--- a/src/Artemis.Core/Services/Storage/ProfileService.cs
+++ b/src/Artemis.Core/Services/Storage/ProfileService.cs
@@ -209,14 +209,15 @@ namespace Artemis.Core.Services
ProcessPendingKeyEvents(profileConfiguration);
// Profiles being edited are updated at their own leisure
- if (profileConfiguration.IsBeingEdited)
+ if (profileConfiguration.IsBeingEdited && RenderForEditor)
continue;
bool shouldBeActive = profileConfiguration.ShouldBeActive(false);
if (shouldBeActive)
{
profileConfiguration.Update();
- shouldBeActive = profileConfiguration.ActivationConditionMet;
+ if (!profileConfiguration.IsBeingEdited)
+ shouldBeActive = profileConfiguration.ActivationConditionMet;
}
try
@@ -245,6 +246,16 @@ namespace Artemis.Core.Services
{
lock (_profileCategories)
{
+ ProfileConfiguration? editedProfileConfiguration = _profileCategories.SelectMany(c => c.ProfileConfigurations).FirstOrDefault(p => p.IsBeingEdited);
+ if (editedProfileConfiguration != null)
+ {
+ editedProfileConfiguration.Profile?.Render(canvas, SKPointI.Empty);
+ return;
+ }
+
+ if (RenderForEditor)
+ return;
+
// Iterate the children in reverse because the first category must be rendered last to end up on top
for (int i = _profileCategories.Count - 1; i > -1; i--)
{
@@ -254,17 +265,9 @@ namespace Artemis.Core.Services
try
{
ProfileConfiguration profileConfiguration = profileCategory.ProfileConfigurations[j];
- if (RenderForEditor)
- {
- if (profileConfiguration.IsBeingEdited)
- profileConfiguration.Profile?.Render(canvas, SKPointI.Empty);
- }
- else
- {
- // Ensure all criteria are met before rendering
- if (!profileConfiguration.IsSuspended && !profileConfiguration.IsMissingModule && profileConfiguration.ActivationConditionMet)
- profileConfiguration.Profile?.Render(canvas, SKPointI.Empty);
- }
+ // Ensure all criteria are met before rendering
+ if (!profileConfiguration.IsSuspended && !profileConfiguration.IsMissingModule && profileConfiguration.ActivationConditionMet)
+ profileConfiguration.Profile?.Render(canvas, SKPointI.Empty);
}
catch (Exception e)
{
@@ -396,13 +399,6 @@ namespace Artemis.Core.Services
}
}
- ///
- /// Creates a new profile configuration and adds it to the provided
- ///
- /// The profile category to add the profile to
- /// The name of the new profile configuration
- /// The icon of the new profile configuration
- /// The newly created profile configuration
public ProfileConfiguration CreateProfileConfiguration(ProfileCategory category, string name, string icon)
{
ProfileConfiguration configuration = new(category, name, icon);
@@ -414,10 +410,6 @@ namespace Artemis.Core.Services
return configuration;
}
- ///
- /// Removes the provided profile configuration from the
- ///
- ///
public void RemoveProfileConfiguration(ProfileConfiguration profileConfiguration)
{
profileConfiguration.Category.RemoveProfileConfiguration(profileConfiguration);
diff --git a/src/Artemis.Storage/Entities/Profile/FolderEntity.cs b/src/Artemis.Storage/Entities/Profile/FolderEntity.cs
index 035210069..0f6cdde1b 100644
--- a/src/Artemis.Storage/Entities/Profile/FolderEntity.cs
+++ b/src/Artemis.Storage/Entities/Profile/FolderEntity.cs
@@ -16,6 +16,7 @@ namespace Artemis.Storage.Entities.Profile
public int Order { get; set; }
public string Name { get; set; }
+ public bool IsExpanded { get; set; }
public bool Suspended { get; set; }
[BsonRef("ProfileEntity")]
diff --git a/src/Artemis.Storage/Entities/Profile/ProfileEntity.cs b/src/Artemis.Storage/Entities/Profile/ProfileEntity.cs
index 0a65c180f..abc390d98 100644
--- a/src/Artemis.Storage/Entities/Profile/ProfileEntity.cs
+++ b/src/Artemis.Storage/Entities/Profile/ProfileEntity.cs
@@ -18,6 +18,7 @@ namespace Artemis.Storage.Entities.Profile
public string Name { get; set; }
public bool IsFreshImport { get; set; }
+ public Guid LastSelectedProfileElement { get; set; }
public List Folders { get; set; }
public List Layers { get; set; }
diff --git a/src/Artemis.UI.Shared/Controls/DraggableFloat.xaml.cs b/src/Artemis.UI.Shared/Controls/DraggableFloat.xaml.cs
index cbc59f4f8..499afb5d7 100644
--- a/src/Artemis.UI.Shared/Controls/DraggableFloat.xaml.cs
+++ b/src/Artemis.UI.Shared/Controls/DraggableFloat.xaml.cs
@@ -27,12 +27,12 @@ namespace Artemis.UI.Shared
///
/// Gets or sets the minimum value
///
- public static readonly DependencyProperty MinProperty = DependencyProperty.Register(nameof(Min), typeof(float?), typeof(DraggableFloat));
+ public static readonly DependencyProperty MinProperty = DependencyProperty.Register(nameof(Min), typeof(object), typeof(DraggableFloat));
///
/// Gets or sets the maximum value
///
- public static readonly DependencyProperty MaxProperty = DependencyProperty.Register(nameof(Max), typeof(float?), typeof(DraggableFloat));
+ public static readonly DependencyProperty MaxProperty = DependencyProperty.Register(nameof(Max), typeof(object), typeof(DraggableFloat));
///
/// Occurs when the value has changed
@@ -90,18 +90,18 @@ namespace Artemis.UI.Shared
///
/// Gets or sets the minimum value
///
- public float? Min
+ public object? Min
{
- get => (float?) GetValue(MinProperty);
+ get => (object?) GetValue(MinProperty);
set => SetValue(MinProperty, value);
}
///
/// Gets or sets the maximum value
///
- public float? Max
+ public object? Max
{
- get => (float?) GetValue(MaxProperty);
+ get => (object?) GetValue(MaxProperty);
set => SetValue(MaxProperty, value);
}
@@ -207,10 +207,11 @@ namespace Artemis.UI.Shared
stepSize = stepSize * 10;
float value = (float) RoundToNearestOf(startValue + stepSize * (x - startX), stepSize);
- if (Min != null)
- value = Math.Max(value, Min.Value);
- if (Max != null)
- value = Math.Min(value, Max.Value);
+
+ if (Min != null && float.TryParse(Min.ToString(), out float minFloat))
+ value = Math.Max(value, minFloat);
+ if (Max != null && float.TryParse(Max.ToString(), out float maxFloat))
+ value = Math.Min(value, maxFloat);
Value = value;
}
diff --git a/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs b/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs
index 0184583d9..d0fdb6e3e 100644
--- a/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs
+++ b/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs
@@ -17,9 +17,17 @@ namespace Artemis.UI.Shared.Services
///
ProfileConfiguration? SelectedProfileConfiguration { get; }
+ ///
+ /// Gets the previous selected profile configuration
+ ///
+ ProfileConfiguration? PreviousSelectedProfileConfiguration { get; }
+
///
/// Gets the currently selected profile
- /// if the editor is closed, always equal to .
+ ///
+ /// if the editor is closed, always equal to .
+ ///
+ ///
///
Profile? SelectedProfile { get; }
@@ -53,6 +61,11 @@ namespace Artemis.UI.Shared.Services
///
bool Playing { get; set; }
+ ///
+ /// Gets or sets a boolean indicating whether editing should be suspended
+ ///
+ bool SuspendEditing { get; set; }
+
///
/// Changes the selected profile by its
///
@@ -214,6 +227,12 @@ namespace Artemis.UI.Shared.Services
///
event EventHandler PixelsPerSecondChanged;
+ ///
+ /// Occurs when the suspend editing boolean is changed
+ ///
+
+ event EventHandler SuspendEditingChanged;
+
///
/// Occurs when the profile preview has been updated
///
diff --git a/src/Artemis.UI.Shared/Services/ProfileEditorService.cs b/src/Artemis.UI.Shared/Services/ProfileEditorService.cs
index ed1a2d68f..f3f3626ea 100644
--- a/src/Artemis.UI.Shared/Services/ProfileEditorService.cs
+++ b/src/Artemis.UI.Shared/Services/ProfileEditorService.cs
@@ -4,7 +4,6 @@ using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using Artemis.Core;
-using Artemis.Core.Modules;
using Artemis.Core.Services;
using Artemis.Storage.Entities.Profile;
using Artemis.UI.Shared.Services.Models;
@@ -30,6 +29,7 @@ namespace Artemis.UI.Shared.Services
private TimeSpan _currentTime;
private bool _doTick;
private int _pixelsPerSecond;
+ private bool _suspendEditing;
public ProfileEditorService(IKernel kernel, ILogger logger, IProfileService profileService, ICoreService coreService, IRgbService rgbService, IModuleService moduleService)
{
@@ -76,7 +76,7 @@ namespace Artemis.UI.Shared.Services
private void Tick()
{
- if (SelectedProfile == null || _doTick)
+ if (SelectedProfile == null || _doTick || SuspendEditing)
return;
TickProfileElement(SelectedProfile.GetRootFolder());
@@ -106,6 +106,32 @@ namespace Artemis.UI.Shared.Services
public ReadOnlyCollection RegisteredPropertyEditors => _registeredPropertyEditors.AsReadOnly();
public bool Playing { get; set; }
+
+ public bool SuspendEditing
+ {
+ get => _suspendEditing;
+ set
+ {
+ if (_suspendEditing == value)
+ return;
+
+ _suspendEditing = value;
+ if (value)
+ {
+ Playing = false;
+ _profileService.RenderForEditor = false;
+ }
+ else
+ {
+ if (SelectedProfileConfiguration != null)
+ _profileService.RenderForEditor = true;
+ }
+
+ OnSuspendEditingChanged();
+ }
+ }
+
+ public ProfileConfiguration? PreviousSelectedProfileConfiguration { get; private set; }
public ProfileConfiguration? SelectedProfileConfiguration { get; private set; }
public Profile? SelectedProfile => SelectedProfileConfiguration?.Profile;
public RenderProfileElement? SelectedProfileElement { get; private set; }
@@ -137,6 +163,9 @@ namespace Artemis.UI.Shared.Services
{
lock (_selectedProfileLock)
{
+ if (SuspendEditing)
+ throw new ArtemisSharedUIException("Cannot change the selected profile while editing is suspended");
+
if (SelectedProfileConfiguration == profileConfiguration)
return;
@@ -144,21 +173,30 @@ namespace Artemis.UI.Shared.Services
throw new ArtemisSharedUIException("Cannot select a disposed profile");
_logger.Verbose("ChangeSelectedProfileConfiguration {profile}", profileConfiguration);
+
+ if (SelectedProfileConfiguration != null)
+ SaveSelectedProfileConfiguration();
+
ChangeSelectedProfileElement(null);
ProfileConfigurationEventArgs profileConfigurationElementEvent = new(profileConfiguration, SelectedProfileConfiguration);
// No need to deactivate the profile, if needed it will be deactivated next update
if (SelectedProfileConfiguration != null)
SelectedProfileConfiguration.IsBeingEdited = false;
-
- // The new profile may need activation
+
+ PreviousSelectedProfileConfiguration = SelectedProfileConfiguration;
SelectedProfileConfiguration = profileConfiguration;
+
+ // The new profile may need activation
if (SelectedProfileConfiguration != null)
{
SelectedProfileConfiguration.IsBeingEdited = true;
_moduleService.SetActivationOverride(SelectedProfileConfiguration.Module);
_profileService.ActivateProfile(SelectedProfileConfiguration);
_profileService.RenderForEditor = true;
+
+ if (SelectedProfileConfiguration.Profile?.LastSelectedProfileElement is RenderProfileElement renderProfileElement)
+ ChangeSelectedProfileElement(renderProfileElement);
}
else
{
@@ -179,6 +217,7 @@ namespace Artemis.UI.Shared.Services
if (SelectedProfile == null)
return;
+ SelectedProfile.LastSelectedProfileElement = SelectedProfileElement;
_profileService.SaveProfile(SelectedProfile, true);
OnSelectedProfileUpdated(new ProfileConfigurationEventArgs(SelectedProfileConfiguration));
UpdateProfilePreview();
@@ -478,6 +517,7 @@ namespace Artemis.UI.Shared.Services
public event EventHandler? SelectedDataBindingChanged;
public event EventHandler? CurrentTimeChanged;
public event EventHandler? PixelsPerSecondChanged;
+ public event EventHandler? SuspendEditingChanged;
public event EventHandler? ProfilePreviewUpdated;
protected virtual void OnSelectedProfileChanged(ProfileConfigurationEventArgs e)
@@ -510,6 +550,11 @@ namespace Artemis.UI.Shared.Services
PixelsPerSecondChanged?.Invoke(this, EventArgs.Empty);
}
+ protected virtual void OnSuspendEditingChanged()
+ {
+ SuspendEditingChanged?.Invoke(this, EventArgs.Empty);
+ }
+
protected virtual void OnProfilePreviewUpdated()
{
ProfilePreviewUpdated?.Invoke(this, EventArgs.Empty);
diff --git a/src/Artemis.UI/Bootstrapper.cs b/src/Artemis.UI/Bootstrapper.cs
index d444e16e2..8f941ef60 100644
--- a/src/Artemis.UI/Bootstrapper.cs
+++ b/src/Artemis.UI/Bootstrapper.cs
@@ -27,6 +27,7 @@ namespace Artemis.UI
{
private ApplicationStateManager _applicationStateManager;
private ICoreService _core;
+ private ILogger _exceptionLogger;
public Bootstrapper()
{
@@ -48,11 +49,11 @@ namespace Artemis.UI
{
_applicationStateManager = new ApplicationStateManager(Kernel, Args);
Core.Utilities.PrepareFirstLaunch();
-
- ILogger logger = Kernel.Get();
+
+ _exceptionLogger = Kernel.Get();
if (_applicationStateManager.FocusExistingInstance())
{
- logger.Information("Shutting down because a different instance is already running.");
+ _exceptionLogger.Information("Shutting down because a different instance is already running.");
Application.Current.Shutdown(1);
return;
}
@@ -63,7 +64,7 @@ namespace Artemis.UI
}
catch (Exception ex)
{
- logger.Error($"Failed to set DPI-Awareness: {ex.Message}");
+ _exceptionLogger.Error($"Failed to set DPI-Awareness: {ex.Message}");
}
IViewManager viewManager = Kernel.Get();
@@ -76,7 +77,7 @@ namespace Artemis.UI
}
catch (Exception e)
{
- HandleFatalException(e, logger);
+ HandleFatalException(e);
throw;
}
@@ -101,7 +102,7 @@ namespace Artemis.UI
}
catch (Exception e)
{
- HandleFatalException(e, logger);
+ HandleFatalException(e);
throw;
}
});
@@ -132,12 +133,11 @@ namespace Artemis.UI
protected override void OnUnhandledException(DispatcherUnhandledExceptionEventArgs e)
{
- ILogger logger = Kernel.Get();
- logger.Fatal(e.Exception, "Unhandled exception");
-
- IDialogService dialogService = Kernel.Get();
try
{
+ _exceptionLogger.Fatal(e.Exception, "Unhandled exception");
+
+ IDialogService dialogService = Kernel.Get();
dialogService.ShowExceptionDialog("Artemis encountered an error", e.Exception);
}
catch (Exception)
@@ -149,9 +149,9 @@ namespace Artemis.UI
e.Handled = true;
}
- private void HandleFatalException(Exception e, ILogger logger)
+ private void HandleFatalException(Exception e)
{
- logger.Fatal(e, "Fatal exception during initialization, shutting down.");
+ _exceptionLogger.Fatal(e, "Fatal exception during initialization, shutting down.");
Execute.OnUIThread(() =>
{
_applicationStateManager.DisplayException(e);
diff --git a/src/Artemis.UI/Converters/LedIdToStringConverter.cs b/src/Artemis.UI/Converters/LedIdToStringConverter.cs
new file mode 100644
index 000000000..154ca4bb9
--- /dev/null
+++ b/src/Artemis.UI/Converters/LedIdToStringConverter.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Globalization;
+using System.Windows.Data;
+using RGB.NET.Core;
+
+namespace Artemis.UI.Converters
+{
+ [ValueConversion(typeof(LedId), typeof(string))]
+ public class LedIdToStringConverter : IValueConverter
+ {
+ #region Implementation of IValueConverter
+
+ ///
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return value?.ToString();
+ }
+
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (Enum.TryParse(typeof(LedId), value?.ToString(), true, out object parsedLedId))
+ return parsedLedId;
+ return LedId.Unknown1;
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Converters/UriToFileNameConverter.cs b/src/Artemis.UI/Converters/UriToFileNameConverter.cs
index 435c1fec5..1118d3eec 100644
--- a/src/Artemis.UI/Converters/UriToFileNameConverter.cs
+++ b/src/Artemis.UI/Converters/UriToFileNameConverter.cs
@@ -5,6 +5,7 @@ using System.Windows.Data;
namespace Artemis.UI.Converters
{
+ [ValueConversion(typeof(Uri), typeof(string))]
public class UriToFileNameConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
diff --git a/src/Artemis.UI/Events/RequestSelectSidebarItemEvent.cs b/src/Artemis.UI/Events/RequestSelectSidebarItemEvent.cs
index 3241ac8f8..2892cfe61 100644
--- a/src/Artemis.UI/Events/RequestSelectSidebarItemEvent.cs
+++ b/src/Artemis.UI/Events/RequestSelectSidebarItemEvent.cs
@@ -1,4 +1,6 @@
-namespace Artemis.UI.Events
+using Artemis.UI.Screens.Sidebar;
+
+namespace Artemis.UI.Events
{
public class RequestSelectSidebarItemEvent
{
@@ -7,6 +9,12 @@
DisplayName = displayName;
}
+ public RequestSelectSidebarItemEvent(SidebarScreenViewModel viewModel)
+ {
+ ViewModel = viewModel;
+ }
+
public string DisplayName { get; }
+ public SidebarScreenViewModel ViewModel { get; }
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesView.xaml b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesView.xaml
index e47af2d72..f5ee47881 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesView.xaml
+++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesView.xaml
@@ -77,7 +77,7 @@
-
+
@@ -444,6 +444,28 @@
Width="319" />
+
+
+
+
+
+
+ Timeline suspended
+
+
+ The profile is currently running in normal mode and the timeline cannot be edited.
+
+
+ Press to switch between editor mode and normal mode. Auto-switching can be disabled in the options menu.
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs
index 5aa6a6515..944111d23 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs
@@ -122,6 +122,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
set => ProfileEditorService.CurrentTime = TimeSpan.FromSeconds(value / ProfileEditorService.PixelsPerSecond);
}
+ public bool SuspendedEditing => ProfileEditorService.SuspendEditing;
+
public int PropertyTreeIndex
{
get => _propertyTreeIndex;
@@ -190,6 +192,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
ProfileEditorService.SelectedProfileElementChanged += SelectedProfileEditorServiceOnSelectedProfileElementChanged;
ProfileEditorService.CurrentTimeChanged += ProfileEditorServiceOnCurrentTimeChanged;
+ ProfileEditorService.SuspendEditingChanged += ProfileEditorServiceOnSuspendEditingChanged;
ProfileEditorService.SelectedDataBindingChanged += ProfileEditorServiceOnSelectedDataBindingChanged;
ProfileEditorService.PixelsPerSecondChanged += ProfileEditorServiceOnPixelsPerSecondChanged;
@@ -200,6 +203,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
{
ProfileEditorService.SelectedProfileElementChanged -= SelectedProfileEditorServiceOnSelectedProfileElementChanged;
ProfileEditorService.CurrentTimeChanged -= ProfileEditorServiceOnCurrentTimeChanged;
+ ProfileEditorService.SuspendEditingChanged -= ProfileEditorServiceOnSuspendEditingChanged;
ProfileEditorService.SelectedDataBindingChanged -= ProfileEditorServiceOnSelectedDataBindingChanged;
ProfileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged;
@@ -230,6 +234,11 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
NotifyOfPropertyChange(nameof(TimeCaretPosition));
}
+ private void ProfileEditorServiceOnSuspendEditingChanged(object? sender, EventArgs e)
+ {
+ NotifyOfPropertyChange(nameof(SuspendedEditing));
+ }
+
private void ProfileEditorServiceOnPixelsPerSecondChanged(object sender, EventArgs e)
{
NotifyOfPropertyChange(nameof(TimeCaretPosition));
@@ -559,6 +568,12 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
private void CoreServiceOnFrameRendering(object sender, FrameRenderingEventArgs e)
{
+ if (!ProfileEditorService.Playing)
+ {
+ CoreService.FrameRendering -= CoreServiceOnFrameRendering;
+ return;
+ }
+
Execute.PostToUIThread(() =>
{
TimeSpan newTime = ProfileEditorService.CurrentTime.Add(TimeSpan.FromSeconds(e.DeltaTime));
diff --git a/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorView.xaml b/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorView.xaml
index b91b42a7f..d63979f9d 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorView.xaml
+++ b/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorView.xaml
@@ -26,6 +26,8 @@
+
+
@@ -60,21 +62,24 @@
s:View.ActionTarget="{Binding ProfileTreeViewModel}" />
-
-
+
-
-
-
@@ -94,21 +99,47 @@
Command="{s:Action Paste}"
InputGestureText="Ctrl+V" />
+
-
-
-
+
+
+
+
+
+
-
@@ -121,7 +152,7 @@
Icon="{materialDesign:PackIcon Kind=Layers}"
Command="{s:Action OpenUrl}"
CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/layers" />
-
@@ -129,7 +160,7 @@
Icon="{materialDesign:PackIcon Kind=Stopwatch}"
Command="{s:Action OpenUrl}"
CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/timeline" />
-
@@ -138,11 +169,11 @@
Command="{s:Action OpenUrl}"
CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/scripting" />
-
-
@@ -152,7 +183,7 @@