diff --git a/src/Artemis.Core/Artemis.Core.csproj b/src/Artemis.Core/Artemis.Core.csproj
index 9a766c559..0c62553d4 100644
--- a/src/Artemis.Core/Artemis.Core.csproj
+++ b/src/Artemis.Core/Artemis.Core.csproj
@@ -44,9 +44,9 @@
-
-
-
+
+
+
diff --git a/src/Artemis.Core/Constants.cs b/src/Artemis.Core/Constants.cs
index f465f118e..cfb3b0551 100644
--- a/src/Artemis.Core/Constants.cs
+++ b/src/Artemis.Core/Constants.cs
@@ -62,8 +62,8 @@ public static class Constants
///
/// The current API version for plugins
///
- public static readonly int PluginApiVersion = int.Parse(CoreAssembly.GetCustomAttributes()
- .First(a => a.Key == "PluginApiVersion").Value);
+ public static readonly int PluginApiVersion = int.Parse(CoreAssembly.GetCustomAttributes().First(a => a.Key == "PluginApiVersion").Value ??
+ throw new InvalidOperationException("Cannot find PluginApiVersion metadata in assembly"));
///
/// The plugin info used by core components of Artemis
diff --git a/src/Artemis.Core/JsonConverters/ForgivingVersionConverter.cs b/src/Artemis.Core/JsonConverters/ForgivingVersionConverter.cs
index 8cfe43151..8a325f7e0 100644
--- a/src/Artemis.Core/JsonConverters/ForgivingVersionConverter.cs
+++ b/src/Artemis.Core/JsonConverters/ForgivingVersionConverter.cs
@@ -10,9 +10,9 @@ namespace Artemis.Core.JsonConverters
///
internal class ForgivingVersionConverter : VersionConverter
{
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
- object obj = base.ReadJson(reader, objectType, existingValue, serializer);
+ object? obj = base.ReadJson(reader, objectType, existingValue, serializer);
if (obj is not Version v)
return obj;
diff --git a/src/Artemis.Core/Models/Profile/Profile.cs b/src/Artemis.Core/Models/Profile/Profile.cs
index 5534e686d..c511f9ec9 100644
--- a/src/Artemis.Core/Models/Profile/Profile.cs
+++ b/src/Artemis.Core/Models/Profile/Profile.cs
@@ -23,7 +23,9 @@ public sealed class Profile : ProfileElement
{
_scripts = new ObservableCollection();
_scriptConfigurations = new ObservableCollection();
-
+
+ Opacity = 0d;
+ ShouldDisplay = true;
Configuration = configuration;
Profile = this;
ProfileEntity = profileEntity;
@@ -81,6 +83,10 @@ public sealed class Profile : ProfileElement
internal List Exceptions { get; }
+ internal bool ShouldDisplay { get; set; }
+
+ internal double Opacity { get; private set; }
+
///
public override void Update(double deltaTime)
{
@@ -97,6 +103,13 @@ public sealed class Profile : ProfileElement
foreach (ProfileScript profileScript in Scripts)
profileScript.OnProfileUpdated(deltaTime);
+
+ const double OPACITY_PER_SECOND = 1;
+
+ if (ShouldDisplay && Opacity < 1)
+ Opacity = Math.Clamp(Opacity + OPACITY_PER_SECOND * deltaTime, 0d, 1d);
+ if (!ShouldDisplay && Opacity > 0)
+ Opacity = Math.Clamp(Opacity - OPACITY_PER_SECOND * deltaTime, 0d, 1d);
}
}
@@ -110,10 +123,26 @@ public sealed class Profile : ProfileElement
foreach (ProfileScript profileScript in Scripts)
profileScript.OnProfileRendering(canvas, canvas.LocalClipBounds);
+
+ SKPaint? opacityPaint = null;
+ bool applyOpacityLayer = Configuration.FadeInAndOut && Opacity < 1;
+
+ if (applyOpacityLayer)
+ {
+ opacityPaint = new SKPaint();
+ opacityPaint.Color = new SKColor(0, 0, 0, (byte)(255d * Easings.CubicEaseInOut(Opacity)));
+ canvas.SaveLayer(opacityPaint);
+ }
foreach (ProfileElement profileElement in Children)
profileElement.Render(canvas, basePosition, editorFocus);
+ if (applyOpacityLayer)
+ {
+ canvas.Restore();
+ opacityPaint?.Dispose();
+ }
+
foreach (ProfileScript profileScript in Scripts)
profileScript.OnProfileRendered(canvas, canvas.LocalClipBounds);
diff --git a/src/Artemis.Core/Models/ProfileConfiguration/ProfileConfiguration.cs b/src/Artemis.Core/Models/ProfileConfiguration/ProfileConfiguration.cs
index 3390a2863..b349acfd1 100644
--- a/src/Artemis.Core/Models/ProfileConfiguration/ProfileConfiguration.cs
+++ b/src/Artemis.Core/Models/ProfileConfiguration/ProfileConfiguration.cs
@@ -21,6 +21,7 @@ public class ProfileConfiguration : BreakableModel, IStorageModel, IDisposable
private bool _isBeingEdited;
private bool _isMissingModule;
private bool _isSuspended;
+ private bool _fadeInAndOut;
private Module? _module;
private string _name;
@@ -160,6 +161,15 @@ public class ProfileConfiguration : BreakableModel, IStorageModel, IDisposable
internal set => SetAndNotify(ref _profile, value);
}
+ ///
+ /// Gets or sets a boolean indicating whether this profile should fade in and out when enabling or disabling
+ ///
+ public bool FadeInAndOut
+ {
+ get => _fadeInAndOut;
+ set => SetAndNotify(ref _fadeInAndOut, value);
+ }
+
///
/// Gets or sets the module this profile uses
///
@@ -272,6 +282,7 @@ public class ProfileConfiguration : BreakableModel, IStorageModel, IDisposable
IsSuspended = Entity.IsSuspended;
ActivationBehaviour = (ActivationBehaviour) Entity.ActivationBehaviour;
HotkeyMode = (ProfileConfigurationHotkeyMode) Entity.HotkeyMode;
+ FadeInAndOut = Entity.FadeInAndOut;
Order = Entity.Order;
Icon.Load();
@@ -294,6 +305,7 @@ public class ProfileConfiguration : BreakableModel, IStorageModel, IDisposable
Entity.ActivationBehaviour = (int) ActivationBehaviour;
Entity.HotkeyMode = (int) HotkeyMode;
Entity.ProfileCategoryId = Category.Entity.Id;
+ Entity.FadeInAndOut = FadeInAndOut;
Entity.Order = Order;
Icon.Save();
diff --git a/src/Artemis.Core/Plugins/PluginFeatureInfo.cs b/src/Artemis.Core/Plugins/PluginFeatureInfo.cs
index 32d5775ec..b1dfbdc7b 100644
--- a/src/Artemis.Core/Plugins/PluginFeatureInfo.cs
+++ b/src/Artemis.Core/Plugins/PluginFeatureInfo.cs
@@ -18,7 +18,6 @@ namespace Artemis.Core;
public class PluginFeatureInfo : CorePropertyChanged, IPrerequisitesSubject
{
private string? _description;
- private string? _icon;
private PluginFeature? _instance;
private Exception? _loadException;
private string _name = null!;
diff --git a/src/Artemis.Core/Services/Input/InputProvider.cs b/src/Artemis.Core/Services/Input/InputProvider.cs
index c51a25d63..8db4dec76 100644
--- a/src/Artemis.Core/Services/Input/InputProvider.cs
+++ b/src/Artemis.Core/Services/Input/InputProvider.cs
@@ -8,7 +8,10 @@ namespace Artemis.Core.Services;
///
public abstract class InputProvider : IDisposable
{
- public InputProvider()
+ ///
+ /// Creates a new instance of the class.
+ ///
+ protected InputProvider()
{
ProviderName = GetType().FullName ?? throw new InvalidOperationException("Input provider must have a type with a name");
}
diff --git a/src/Artemis.Core/Services/Storage/ProfileService.cs b/src/Artemis.Core/Services/Storage/ProfileService.cs
index d5adc7d87..4b86f3d55 100644
--- a/src/Artemis.Core/Services/Storage/ProfileService.cs
+++ b/src/Artemis.Core/Services/Storage/ProfileService.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
@@ -213,8 +213,17 @@ internal class ProfileService : IProfileService
// Make sure the profile is active or inactive according to the parameters above
if (shouldBeActive && profileConfiguration.Profile == null && profileConfiguration.BrokenState != "Failed to activate profile")
profileConfiguration.TryOrBreak(() => ActivateProfile(profileConfiguration), "Failed to activate profile");
+ if (shouldBeActive && profileConfiguration.Profile != null && !profileConfiguration.Profile.ShouldDisplay)
+ profileConfiguration.Profile.ShouldDisplay = true;
else if (!shouldBeActive && profileConfiguration.Profile != null)
- DeactivateProfile(profileConfiguration);
+ {
+ if (!profileConfiguration.FadeInAndOut)
+ DeactivateProfile(profileConfiguration);
+ else if (!profileConfiguration.Profile.ShouldDisplay && profileConfiguration.Profile.Opacity <= 0)
+ DeactivateProfile(profileConfiguration);
+ else if (profileConfiguration.Profile.Opacity > 0)
+ RequestDeactivation(profileConfiguration);
+ }
profileConfiguration.Profile?.Update(deltaTime);
}
@@ -254,7 +263,8 @@ internal class ProfileService : IProfileService
{
ProfileConfiguration profileConfiguration = profileCategory.ProfileConfigurations[j];
// Ensure all criteria are met before rendering
- if (!profileConfiguration.IsSuspended && !profileConfiguration.IsMissingModule && profileConfiguration.ActivationConditionMet)
+ bool fadingOut = profileConfiguration.Profile?.ShouldDisplay == false && profileConfiguration.Profile?.Opacity > 0;
+ if (!profileConfiguration.IsSuspended && !profileConfiguration.IsMissingModule && (profileConfiguration.ActivationConditionMet || fadingOut))
profileConfiguration.Profile?.Render(canvas, SKPointI.Empty, null);
}
catch (Exception e)
@@ -316,7 +326,10 @@ internal class ProfileService : IProfileService
public Profile ActivateProfile(ProfileConfiguration profileConfiguration)
{
if (profileConfiguration.Profile != null)
+ {
+ profileConfiguration.Profile.ShouldDisplay = true;
return profileConfiguration.Profile;
+ }
ProfileEntity profileEntity;
try
@@ -361,6 +374,16 @@ internal class ProfileService : IProfileService
OnProfileDeactivated(new ProfileConfigurationEventArgs(profileConfiguration));
}
+ public void RequestDeactivation(ProfileConfiguration profileConfiguration)
+ {
+ if (profileConfiguration.IsBeingEdited)
+ throw new ArtemisCoreException("Cannot disable a profile that is being edited, that's rude");
+ if (profileConfiguration.Profile == null)
+ return;
+
+ profileConfiguration.Profile.ShouldDisplay = false;
+ }
+
public void DeleteProfile(ProfileConfiguration profileConfiguration)
{
DeactivateProfile(profileConfiguration);
diff --git a/src/Artemis.Core/VisualScripting/Interfaces/INode.cs b/src/Artemis.Core/VisualScripting/Interfaces/INode.cs
index dce2df631..3bc2521fb 100644
--- a/src/Artemis.Core/VisualScripting/Interfaces/INode.cs
+++ b/src/Artemis.Core/VisualScripting/Interfaces/INode.cs
@@ -97,7 +97,7 @@ public interface INode : INotifyPropertyChanged, IBreakableModel
void TryEvaluate();
///
- /// Resets the node causing all pins to re-evaluate the next time is called
+ /// Resets the node causing all pins to re-evaluate the next time is called
///
void Reset();
}
\ No newline at end of file
diff --git a/src/Artemis.Storage/Entities/Profile/ProfileConfigurationEntity.cs b/src/Artemis.Storage/Entities/Profile/ProfileConfigurationEntity.cs
index 377d78ce7..e91ddcc96 100644
--- a/src/Artemis.Storage/Entities/Profile/ProfileConfigurationEntity.cs
+++ b/src/Artemis.Storage/Entities/Profile/ProfileConfigurationEntity.cs
@@ -25,4 +25,6 @@ public class ProfileConfigurationEntity
public Guid ProfileCategoryId { get; set; }
public Guid ProfileId { get; set; }
+
+ public bool FadeInAndOut { get; set; }
}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj b/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj
index 66fe3e2c7..09c2d5479 100644
--- a/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj
+++ b/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj
@@ -20,7 +20,7 @@
-
+
diff --git a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs
index c3fd4fc6e..1632f25a7 100644
--- a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs
+++ b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs
@@ -317,7 +317,7 @@ public class DeviceVisualizer : Control
Dispatcher.UIThread.Post(InvalidateMeasure);
}
- catch (Exception e)
+ catch (Exception)
{
// ignored
}
diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs
index a1df2555c..029a6dc18 100644
--- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs
+++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs
@@ -18,7 +18,7 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared;
///
public abstract class DataModelVisualizationViewModel : ReactiveObject, IDisposable
{
- private const int MaxDepth = 4;
+ private const int MAX_DEPTH = 4;
private ObservableCollection _children;
private DataModel? _dataModel;
private bool _isMatchingFilteredTypes;
@@ -47,6 +47,9 @@ public abstract class DataModelVisualizationViewModel : ReactiveObject, IDisposa
PropertyDescription = DataModelPath?.GetPropertyDescription() ?? DataModel?.DataModelDescription;
}
+ ///
+ /// Copies the path of the data model to the clipboard.
+ ///
public ReactiveCommand CopyPath { get; }
///
@@ -337,7 +340,7 @@ public abstract class DataModelVisualizationViewModel : ReactiveObject, IDisposa
{
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 > MAX_DEPTH)
return null;
DataModelPath dataModelPath = new(DataModel, path);
diff --git a/src/Artemis.UI.Shared/ReactiveCoreWindow.cs b/src/Artemis.UI.Shared/ReactiveCoreWindow.cs
index 60fd8f353..295f4e6bb 100644
--- a/src/Artemis.UI.Shared/ReactiveCoreWindow.cs
+++ b/src/Artemis.UI.Shared/ReactiveCoreWindow.cs
@@ -19,6 +19,9 @@ namespace Artemis.UI.Shared;
/// ViewModel type.
public class ReactiveCoreWindow : CoreWindow, IViewFor where TViewModel : class
{
+ ///
+ /// The ViewModel.
+ ///
public static readonly StyledProperty ViewModelProperty = AvaloniaProperty
.Register, TViewModel?>(nameof(ViewModel));
diff --git a/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateColorGradient.cs b/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateColorGradient.cs
index 5970b652b..58f91f4bd 100644
--- a/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateColorGradient.cs
+++ b/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateColorGradient.cs
@@ -5,7 +5,7 @@ using Artemis.Core;
namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
///
-/// Represents a profile editor command that can be used to update a layer property of type .
+/// Represents a profile editor command that can be used to update a color gradient.
///
public class UpdateColorGradient : IProfileEditorCommand
{
diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj
index c568a677c..04e578c41 100644
--- a/src/Artemis.UI/Artemis.UI.csproj
+++ b/src/Artemis.UI/Artemis.UI.csproj
@@ -29,8 +29,8 @@
-
-
+
+
diff --git a/src/Artemis.UI/Screens/Root/RootViewModel.cs b/src/Artemis.UI/Screens/Root/RootViewModel.cs
index 10481f25c..b9175e9ed 100644
--- a/src/Artemis.UI/Screens/Root/RootViewModel.cs
+++ b/src/Artemis.UI/Screens/Root/RootViewModel.cs
@@ -92,7 +92,7 @@ public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvi
private void CurrentMainWindowOnClosing(object? sender, EventArgs e)
{
- WindowSizeSetting.Save();
+ WindowSizeSetting?.Save();
_lifeTime.MainWindow = null;
SidebarViewModel = null;
Router.NavigateAndReset.Execute(new EmptyViewModel(this, "blank")).Subscribe();
diff --git a/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileConfigurationEditView.axaml b/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileConfigurationEditView.axaml
index fed01edac..a127cdc14 100644
--- a/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileConfigurationEditView.axaml
+++ b/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileConfigurationEditView.axaml
@@ -127,6 +127,9 @@
+
+ Fade when enabling and disabling
+
diff --git a/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileConfigurationEditViewModel.cs b/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileConfigurationEditViewModel.cs
index ed55d14c3..eaaf00480 100644
--- a/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileConfigurationEditViewModel.cs
+++ b/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileConfigurationEditViewModel.cs
@@ -30,6 +30,7 @@ public class ProfileConfigurationEditViewModel : DialogViewModelBase? _materialIcons;
@@ -57,6 +58,7 @@ public class ProfileConfigurationEditViewModel : DialogViewModelBase RaiseAndSetIfChanged(ref _disableHotkey, value);
}
+ public bool FadeInAndOut
+ {
+ get => _fadeInAndOut;
+ set => RaiseAndSetIfChanged(ref _fadeInAndOut, value);
+ }
+
public ObservableCollection Modules { get; }
public ProfileModuleViewModel? SelectedModule
@@ -131,7 +139,6 @@ public class ProfileConfigurationEditViewModel : DialogViewModelBase OpenConditionEditor { get; }
public ReactiveCommand BrowseBitmapFile { get; }
public ReactiveCommand Confirm { get; }
- public ReactiveCommand Import { get; }
public ReactiveCommand Delete { get; }
public ReactiveCommand Cancel { get; }
@@ -155,6 +162,7 @@ public class ProfileConfigurationEditViewModel : DialogViewModelBase
- private void CreateOrAddOutputPin(Type valueType, string displayName)
+ private new void CreateOrAddOutputPin(Type valueType, string displayName)
{
// Grab the first pin from the bucket that isn't on the node yet
OutputPin? pin = _pinBucket.FirstOrDefault(p => !Pins.Contains(p));
diff --git a/src/Artemis.VisualScripting/Nodes/List/ListOperatorPredicateNode.cs b/src/Artemis.VisualScripting/Nodes/List/ListOperatorPredicateNode.cs
index 90f4f83eb..3631e5934 100644
--- a/src/Artemis.VisualScripting/Nodes/List/ListOperatorPredicateNode.cs
+++ b/src/Artemis.VisualScripting/Nodes/List/ListOperatorPredicateNode.cs
@@ -9,7 +9,7 @@ namespace Artemis.VisualScripting.Nodes.List;
public class ListOperatorPredicateNode : Node, IDisposable
{
private readonly object _scriptLock = new();
- private ListOperatorPredicateStartNode _startNode;
+ private readonly ListOperatorPredicateStartNode _startNode;
public ListOperatorPredicateNode()
{
@@ -65,7 +65,7 @@ public class ListOperatorPredicateNode : Node