diff --git a/src/Artemis.Core/Models/Profile/Profile.cs b/src/Artemis.Core/Models/Profile/Profile.cs index 5534e686d..7bbde4bd3 100644 --- a/src/Artemis.Core/Models/Profile/Profile.cs +++ b/src/Artemis.Core/Models/Profile/Profile.cs @@ -8,6 +8,15 @@ using SkiaSharp; namespace Artemis.Core; + +internal enum FadingStatus +{ + Enabled, + FadingIn, + FadingOut, + Disabled +} + /// /// Represents a profile containing folders and layers /// @@ -18,12 +27,15 @@ public sealed class Profile : ProfileElement private readonly ObservableCollection _scripts; private bool _isFreshImport; private ProfileElement? _lastSelectedProfileElement; + private double _opacity; internal Profile(ProfileConfiguration configuration, ProfileEntity profileEntity) : base(null!) { _scripts = new ObservableCollection(); _scriptConfigurations = new ObservableCollection(); - + _opacity = 0d; + + FadingStatus = FadingStatus.FadingIn; Configuration = configuration; Profile = this; ProfileEntity = profileEntity; @@ -81,6 +93,8 @@ public sealed class Profile : ProfileElement internal List Exceptions { get; } + internal FadingStatus FadingStatus { get; private set; } + /// public override void Update(double deltaTime) { @@ -97,6 +111,16 @@ public sealed class Profile : ProfileElement foreach (ProfileScript profileScript in Scripts) profileScript.OnProfileUpdated(deltaTime); + + const double OPACITY_PER_SECOND = 1; + if (FadingStatus == FadingStatus.FadingIn) + _opacity = Math.Clamp(_opacity + OPACITY_PER_SECOND * deltaTime, 0d, 1d); + if (FadingStatus == FadingStatus.FadingOut) + _opacity = Math.Clamp(_opacity - OPACITY_PER_SECOND * deltaTime, 0d, 1d); + if (_opacity == 0d) + FadingStatus = FadingStatus.Disabled; + if (_opacity == 1d) + FadingStatus = FadingStatus.Enabled; } } @@ -111,9 +135,17 @@ public sealed class Profile : ProfileElement foreach (ProfileScript profileScript in Scripts) profileScript.OnProfileRendering(canvas, canvas.LocalClipBounds); + using var opacityPaint = new SKPaint(); + if (Configuration.FadeInAndOut && FadingStatus != FadingStatus.Enabled) + 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); + canvas.Restore(); + foreach (ProfileScript profileScript in Scripts) profileScript.OnProfileRendered(canvas, canvas.LocalClipBounds); @@ -165,6 +197,17 @@ public sealed class Profile : ProfileElement layer.PopulateLeds(devices); } + /// + /// Starts the fade out process. + /// + public void FadeOut() + { + if (Disposed) + throw new ObjectDisposedException("Profile"); + + FadingStatus = FadingStatus.FadingOut; + } + #region Overrides of BreakableModel /// 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/Services/Storage/ProfileService.cs b/src/Artemis.Core/Services/Storage/ProfileService.cs index d5adc7d87..6a0c2ad52 100644 --- a/src/Artemis.Core/Services/Storage/ProfileService.cs +++ b/src/Artemis.Core/Services/Storage/ProfileService.cs @@ -214,7 +214,12 @@ internal class ProfileService : IProfileService if (shouldBeActive && profileConfiguration.Profile == null && profileConfiguration.BrokenState != "Failed to activate profile") profileConfiguration.TryOrBreak(() => ActivateProfile(profileConfiguration), "Failed to activate profile"); else if (!shouldBeActive && profileConfiguration.Profile != null) - DeactivateProfile(profileConfiguration); + { + if (!profileConfiguration.FadeInAndOut || profileConfiguration.Profile.FadingStatus == FadingStatus.Disabled) + DeactivateProfile(profileConfiguration); + else if (profileConfiguration.Profile.FadingStatus == FadingStatus.Enabled) + RequestDeactivation(profileConfiguration); + } profileConfiguration.Profile?.Update(deltaTime); } @@ -254,7 +259,7 @@ internal class ProfileService : IProfileService { ProfileConfiguration profileConfiguration = profileCategory.ProfileConfigurations[j]; // Ensure all criteria are met before rendering - if (!profileConfiguration.IsSuspended && !profileConfiguration.IsMissingModule && profileConfiguration.ActivationConditionMet) + if (!profileConfiguration.IsSuspended && !profileConfiguration.IsMissingModule && (profileConfiguration.ActivationConditionMet || profileConfiguration.Profile?.FadingStatus == FadingStatus.FadingOut)) profileConfiguration.Profile?.Render(canvas, SKPointI.Empty, null); } catch (Exception e) @@ -361,6 +366,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.FadeOut(); + } + public void DeleteProfile(ProfileConfiguration profileConfiguration) { DeactivateProfile(profileConfiguration); 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/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..fe773c4f5 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 @@ -155,6 +163,7 @@ public class ProfileConfigurationEditViewModel : DialogViewModelBase