mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 19:36:28 +00:00
Profiles - Reworked profile system
Sidebar - Redesigned sidebar with customizable categories Profiles - Added the ability to configure custom profile icons Profiles - Added the ability to activate multiple profiles for modules at once Profiles - Added the ability to create profiles for no modules Profiles - Added the ability to suspend a profile or an entire category Profiles - Added profile activation conditions Profiles - Added file-based importing/exporting Profile editor - Condensed UI, removed tabs Profile editor - Disable condition operators until a left-side is picked
This commit is contained in:
1 parent
4152c290d2
commit
ceeaa4bf6d
164 files changed
+5019
-4220
No files matched your search
+49
-20
@@ -105,37 +105,66 @@ namespace Artemis.Core
|
||||
InitializeRightPath();
|
||||
// Right side static
|
||||
else if (PredicateType == ProfileRightSideType.Static && Entity.RightStaticValue != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (LeftPath != null && LeftPath.IsValid)
|
||||
// If the left path is not valid we cannot reliably set up the right side because the type is unknown
|
||||
// Because of that wait for it to validate first
|
||||
if (LeftPath != null && !LeftPath.IsValid)
|
||||
{
|
||||
// Use the left side type so JSON.NET has a better idea what to do
|
||||
Type leftSideType = LeftPath.GetPropertyType()!;
|
||||
object? rightSideValue;
|
||||
|
||||
try
|
||||
{
|
||||
rightSideValue = CoreJson.DeserializeObject(Entity.RightStaticValue, leftSideType);
|
||||
}
|
||||
// If deserialization fails, use the type's default
|
||||
catch (JsonSerializationException e)
|
||||
{
|
||||
DeserializationLogger.LogPredicateDeserializationFailure(this, e);
|
||||
rightSideValue = Activator.CreateInstance(leftSideType);
|
||||
}
|
||||
|
||||
UpdateRightSideStatic(rightSideValue);
|
||||
LeftPath.PathValidated += InitializeRightSideStatic;
|
||||
return;
|
||||
}
|
||||
else
|
||||
if (LeftPath == null)
|
||||
return;
|
||||
|
||||
// Use the left side type so JSON.NET has a better idea what to do
|
||||
Type leftSideType = LeftPath.GetPropertyType()!;
|
||||
object? rightSideValue;
|
||||
|
||||
try
|
||||
{
|
||||
// Hope for the best...
|
||||
UpdateRightSideStatic(CoreJson.DeserializeObject(Entity.RightStaticValue));
|
||||
rightSideValue = CoreJson.DeserializeObject(Entity.RightStaticValue, leftSideType);
|
||||
}
|
||||
// If deserialization fails, use the type's default
|
||||
catch (JsonSerializationException e)
|
||||
{
|
||||
DeserializationLogger.LogPredicateDeserializationFailure(this, e);
|
||||
rightSideValue = Activator.CreateInstance(leftSideType);
|
||||
}
|
||||
|
||||
UpdateRightSideStatic(rightSideValue);
|
||||
}
|
||||
catch (JsonReaderException e)
|
||||
{
|
||||
DeserializationLogger.LogPredicateDeserializationFailure(this, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeRightSideStatic(object? sender, EventArgs args)
|
||||
{
|
||||
if (LeftPath == null)
|
||||
return;
|
||||
|
||||
LeftPath.PathValidated -= InitializeRightSideStatic;
|
||||
|
||||
// Use the left side type so JSON.NET has a better idea what to do
|
||||
Type leftSideType = LeftPath.GetPropertyType()!;
|
||||
object? rightSideValue;
|
||||
|
||||
try
|
||||
{
|
||||
rightSideValue = CoreJson.DeserializeObject(Entity.RightStaticValue, leftSideType);
|
||||
}
|
||||
// If deserialization fails, use the type's default
|
||||
catch (JsonSerializationException e)
|
||||
{
|
||||
DeserializationLogger.LogPredicateDeserializationFailure(this, e);
|
||||
rightSideValue = Activator.CreateInstance(leftSideType);
|
||||
}
|
||||
|
||||
UpdateRightSideStatic(rightSideValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ namespace Artemis.Core
|
||||
{
|
||||
internal EventPredicateWrapperDataModel()
|
||||
{
|
||||
Feature = Constants.CorePluginFeature;
|
||||
Module = Constants.CorePluginFeature;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ namespace Artemis.Core
|
||||
{
|
||||
internal ListPredicateWrapperDataModel()
|
||||
{
|
||||
Feature = Constants.CorePluginFeature;
|
||||
Module = Constants.CorePluginFeature;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace Artemis.Core
|
||||
/// <summary>
|
||||
/// Gets the data model ID of the <see cref="Target" /> if it is a <see cref="DataModel" />
|
||||
/// </summary>
|
||||
public string? DataModelId => Target?.Feature.Id;
|
||||
public string? DataModelId => Target?.Module.Id;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the point-separated path associated with this <see cref="DataModelPath" />
|
||||
@@ -327,7 +327,7 @@ namespace Artemis.Core
|
||||
|
||||
private void DataModelStoreOnDataModelAdded(object? sender, DataModelStoreEvent e)
|
||||
{
|
||||
if (e.Registration.DataModel.Feature.Id != Entity.DataModelId)
|
||||
if (e.Registration.DataModel.Module.Id != Entity.DataModelId)
|
||||
return;
|
||||
|
||||
Target = e.Registration.DataModel;
|
||||
@@ -336,7 +336,7 @@ namespace Artemis.Core
|
||||
|
||||
private void DataModelStoreOnDataModelRemoved(object? sender, DataModelStoreEvent e)
|
||||
{
|
||||
if (e.Registration.DataModel.Feature.Id != Entity.DataModelId)
|
||||
if (e.Registration.DataModel.Module.Id != Entity.DataModelId)
|
||||
return;
|
||||
|
||||
Target = null;
|
||||
|
||||
@@ -205,13 +205,6 @@ namespace Artemis.Core
|
||||
canvas.SaveLayer(layerPaint);
|
||||
canvas.Translate(Bounds.Left - basePosition.X, Bounds.Top - basePosition.Y);
|
||||
|
||||
// If required, apply the opacity override of the module to the root folder
|
||||
if (IsRootFolder && Profile.Module.OpacityOverride < 1)
|
||||
{
|
||||
double multiplier = Easings.SineEaseInOut(Profile.Module.OpacityOverride);
|
||||
layerPaint.Color = layerPaint.Color.WithAlpha((byte) (layerPaint.Color.Alpha * multiplier));
|
||||
}
|
||||
|
||||
// No point rendering if the alpha was set to zero by one of the effects
|
||||
if (layerPaint.Color.Alpha == 0)
|
||||
return;
|
||||
@@ -240,6 +233,7 @@ namespace Artemis.Core
|
||||
{
|
||||
Disposed = true;
|
||||
|
||||
Disable();
|
||||
foreach (ProfileElement profileElement in Children)
|
||||
profileElement.Dispose();
|
||||
|
||||
|
||||
@@ -168,6 +168,8 @@ namespace Artemis.Core
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
Disable();
|
||||
|
||||
Disposed = true;
|
||||
|
||||
// Brush first in case it depends on any of the other disposables during it's own disposal
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using SkiaSharp;
|
||||
|
||||
@@ -13,31 +12,15 @@ namespace Artemis.Core
|
||||
public sealed class Profile : ProfileElement
|
||||
{
|
||||
private readonly object _lock = new();
|
||||
private bool _isActivated;
|
||||
private bool _isFreshImport;
|
||||
|
||||
internal Profile(ProfileModule module, string name) : base(null!)
|
||||
{
|
||||
ProfileEntity = new ProfileEntity();
|
||||
EntityId = Guid.NewGuid();
|
||||
|
||||
Profile = this;
|
||||
Module = module;
|
||||
Name = name;
|
||||
UndoStack = new Stack<string>();
|
||||
RedoStack = new Stack<string>();
|
||||
|
||||
Folder _ = new(this, "Root folder");
|
||||
Save();
|
||||
}
|
||||
|
||||
internal Profile(ProfileModule module, ProfileEntity profileEntity) : base(null!)
|
||||
|
||||
internal Profile(ProfileConfiguration configuration, ProfileEntity profileEntity) : base(null!)
|
||||
{
|
||||
Configuration = configuration;
|
||||
Profile = this;
|
||||
ProfileEntity = profileEntity;
|
||||
EntityId = profileEntity.Id;
|
||||
|
||||
Module = module;
|
||||
UndoStack = new Stack<string>();
|
||||
RedoStack = new Stack<string>();
|
||||
|
||||
@@ -45,18 +28,9 @@ namespace Artemis.Core
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the module backing this profile
|
||||
/// Gets the profile configuration of this profile
|
||||
/// </summary>
|
||||
public ProfileModule Module { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether this profile is activated
|
||||
/// </summary>
|
||||
public bool IsActivated
|
||||
{
|
||||
get => _isActivated;
|
||||
private set => SetAndNotify(ref _isActivated, value);
|
||||
}
|
||||
public ProfileConfiguration Configuration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether this profile is freshly imported i.e. no changes have been made to it
|
||||
@@ -87,8 +61,6 @@ namespace Artemis.Core
|
||||
{
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Profile");
|
||||
if (!IsActivated)
|
||||
throw new ArtemisCoreException($"Cannot update inactive profile: {this}");
|
||||
|
||||
foreach (ProfileElement profileElement in Children)
|
||||
profileElement.Update(deltaTime);
|
||||
@@ -102,8 +74,6 @@ namespace Artemis.Core
|
||||
{
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Profile");
|
||||
if (!IsActivated)
|
||||
throw new ArtemisCoreException($"Cannot render inactive profile: {this}");
|
||||
|
||||
foreach (ProfileElement profileElement in Children)
|
||||
profileElement.Render(canvas, basePosition);
|
||||
@@ -133,7 +103,7 @@ namespace Artemis.Core
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"[Profile] {nameof(Name)}: {Name}, {nameof(IsActivated)}: {IsActivated}, {nameof(Module)}: {Module}";
|
||||
return $"[Profile] {nameof(Name)}: {Name}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -149,29 +119,15 @@ namespace Artemis.Core
|
||||
layer.PopulateLeds(devices);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the profile has been activated.
|
||||
/// </summary>
|
||||
public event EventHandler? Activated;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the profile is being deactivated.
|
||||
/// </summary>
|
||||
public event EventHandler? Deactivated;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
OnDeactivating();
|
||||
|
||||
foreach (ProfileElement profileElement in Children)
|
||||
profileElement.Dispose();
|
||||
ChildrenList.Clear();
|
||||
|
||||
IsActivated = false;
|
||||
Disposed = true;
|
||||
}
|
||||
|
||||
@@ -180,7 +136,7 @@ namespace Artemis.Core
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Profile");
|
||||
|
||||
Name = ProfileEntity.Name;
|
||||
Name = Configuration.Name;
|
||||
IsFreshImport = ProfileEntity.IsFreshImport;
|
||||
|
||||
lock (ChildrenList)
|
||||
@@ -197,7 +153,9 @@ namespace Artemis.Core
|
||||
Folder _ = new(this, "Root folder");
|
||||
}
|
||||
else
|
||||
{
|
||||
AddChild(new Folder(this, this, rootFolder));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,9 +165,7 @@ namespace Artemis.Core
|
||||
throw new ObjectDisposedException("Profile");
|
||||
|
||||
ProfileEntity.Id = EntityId;
|
||||
ProfileEntity.ModuleId = Module.Id;
|
||||
ProfileEntity.Name = Name;
|
||||
ProfileEntity.IsActive = IsActivated;
|
||||
ProfileEntity.Name = Configuration.Name;
|
||||
ProfileEntity.IsFreshImport = IsFreshImport;
|
||||
|
||||
foreach (ProfileElement profileElement in Children)
|
||||
@@ -221,30 +177,5 @@ namespace Artemis.Core
|
||||
ProfileEntity.Layers.Clear();
|
||||
ProfileEntity.Layers.AddRange(GetAllLayers().Select(f => f.LayerEntity));
|
||||
}
|
||||
|
||||
internal void Activate(IEnumerable<ArtemisDevice> devices)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Profile");
|
||||
if (IsActivated)
|
||||
return;
|
||||
|
||||
PopulateLeds(devices);
|
||||
OnActivated();
|
||||
IsActivated = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnActivated()
|
||||
{
|
||||
Activated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void OnDeactivating()
|
||||
{
|
||||
Deactivated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
public class ProfileCategory : CorePropertyChanged, IStorageModel
|
||||
{
|
||||
private readonly List<ProfileConfiguration> _profileConfigurations = new();
|
||||
private bool _isCollapsed;
|
||||
private bool _isSuspended;
|
||||
private string _name;
|
||||
private int _order;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="ProfileCategory" /> class
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the category</param>
|
||||
internal ProfileCategory(string name)
|
||||
{
|
||||
_name = name;
|
||||
Entity = new ProfileCategoryEntity();
|
||||
}
|
||||
|
||||
internal ProfileCategory(ProfileCategoryEntity entity)
|
||||
{
|
||||
Entity = entity;
|
||||
Load();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the profile category
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => SetAndNotify(ref _name, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The order in which this category appears in the update loop and sidebar
|
||||
/// </summary>
|
||||
public int Order
|
||||
{
|
||||
get => _order;
|
||||
set => SetAndNotify(ref _order, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the category is collapsed or not
|
||||
/// <para>Note: Has no implications other than inside the UI</para>
|
||||
/// </summary>
|
||||
public bool IsCollapsed
|
||||
{
|
||||
get => _isCollapsed;
|
||||
set => SetAndNotify(ref _isCollapsed, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether this category is suspended, disabling all its profiles
|
||||
/// </summary>
|
||||
public bool IsSuspended
|
||||
{
|
||||
get => _isSuspended;
|
||||
set => SetAndNotify(ref _isSuspended, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read only collection of the profiles inside this category
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<ProfileConfiguration> ProfileConfigurations => _profileConfigurations.AsReadOnly();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique ID of this category
|
||||
/// </summary>
|
||||
public Guid EntityId => Entity.Id;
|
||||
|
||||
internal ProfileCategoryEntity Entity { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds a profile configuration to this category
|
||||
/// </summary>
|
||||
public void AddProfileConfiguration(ProfileConfiguration configuration, int? targetIndex)
|
||||
{
|
||||
// Removing the original will shift every item in the list forwards, keep that in mind with the target index
|
||||
if (configuration.Category == this && targetIndex != null && targetIndex.Value > _profileConfigurations.IndexOf(configuration))
|
||||
targetIndex -= 1;
|
||||
|
||||
configuration.Category.RemoveProfileConfiguration(configuration);
|
||||
|
||||
if (targetIndex != null)
|
||||
_profileConfigurations.Insert(Math.Clamp(targetIndex.Value, 0, _profileConfigurations.Count), configuration);
|
||||
else
|
||||
_profileConfigurations.Add(configuration);
|
||||
configuration.Category = this;
|
||||
|
||||
for (int index = 0; index < _profileConfigurations.Count; index++)
|
||||
_profileConfigurations[index].Order = index;
|
||||
OnProfileConfigurationAdded(new ProfileConfigurationEventArgs(configuration));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"[ProfileCategory] {Order} {nameof(Name)}: {Name}, {nameof(IsSuspended)}: {IsSuspended}";
|
||||
}
|
||||
|
||||
internal void RemoveProfileConfiguration(ProfileConfiguration configuration)
|
||||
{
|
||||
if (!_profileConfigurations.Remove(configuration)) return;
|
||||
|
||||
for (int index = 0; index < _profileConfigurations.Count; index++)
|
||||
_profileConfigurations[index].Order = index;
|
||||
OnProfileConfigurationRemoved(new ProfileConfigurationEventArgs(configuration));
|
||||
}
|
||||
|
||||
#region Implementation of IStorageModel
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Load()
|
||||
{
|
||||
Name = Entity.Name;
|
||||
IsCollapsed = Entity.IsCollapsed;
|
||||
IsSuspended = Entity.IsSuspended;
|
||||
Order = Entity.Order;
|
||||
|
||||
_profileConfigurations.Clear();
|
||||
foreach (ProfileConfigurationEntity entityProfileConfiguration in Entity.ProfileConfigurations)
|
||||
_profileConfigurations.Add(new ProfileConfiguration(this, entityProfileConfiguration));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Save()
|
||||
{
|
||||
Entity.Name = Name;
|
||||
Entity.IsCollapsed = IsCollapsed;
|
||||
Entity.IsSuspended = IsSuspended;
|
||||
Entity.Order = Order;
|
||||
|
||||
Entity.ProfileConfigurations.Clear();
|
||||
foreach (ProfileConfiguration profileConfiguration in ProfileConfigurations)
|
||||
{
|
||||
profileConfiguration.Save();
|
||||
Entity.ProfileConfigurations.Add(profileConfiguration.Entity);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a profile configuration is added to this <see cref="ProfileCategory" />
|
||||
/// </summary>
|
||||
public event EventHandler<ProfileConfigurationEventArgs>? ProfileConfigurationAdded;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a profile configuration is removed from this <see cref="ProfileCategory" />
|
||||
/// </summary>
|
||||
public event EventHandler<ProfileConfigurationEventArgs>? ProfileConfigurationRemoved;
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="ProfileConfigurationAdded" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnProfileConfigurationAdded(ProfileConfigurationEventArgs e)
|
||||
{
|
||||
ProfileConfigurationAdded?.Invoke(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="ProfileConfigurationRemoved" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnProfileConfigurationRemoved(ProfileConfigurationEventArgs e)
|
||||
{
|
||||
ProfileConfigurationRemoved?.Invoke(this, e);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a name of one of the default categories
|
||||
/// </summary>
|
||||
public enum DefaultCategoryName
|
||||
{
|
||||
/// <summary>
|
||||
/// The category used by profiles tied to games
|
||||
/// </summary>
|
||||
Games,
|
||||
|
||||
/// <summary>
|
||||
/// The category used by profiles tied to applications
|
||||
/// </summary>
|
||||
Applications,
|
||||
|
||||
/// <summary>
|
||||
/// The category used by general profiles
|
||||
/// </summary>
|
||||
General
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a type of behaviour when this profile is activated
|
||||
/// </summary>
|
||||
public enum ActivationBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Do nothing to other profiles
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Disable all other profiles
|
||||
/// </summary>
|
||||
DisableOthers,
|
||||
|
||||
/// <summary>
|
||||
/// Disable all other profiles below this one
|
||||
/// </summary>
|
||||
DisableOthersBelow,
|
||||
|
||||
/// <summary>
|
||||
/// Disable all other profiles above this one
|
||||
/// </summary>
|
||||
DisableOthersAbove,
|
||||
|
||||
/// <summary>
|
||||
/// Disable all other profiles in the same category
|
||||
/// </summary>
|
||||
DisableOthersInCategory,
|
||||
|
||||
/// <summary>
|
||||
/// Disable all other profiles below this one in the same category
|
||||
/// </summary>
|
||||
DisableOthersBelowInCategory,
|
||||
|
||||
/// <summary>
|
||||
/// Disable all other profiles above this one in the same category
|
||||
/// </summary>
|
||||
DisableOthersAboveInCategory
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
using System;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a descriptor that describes a profile
|
||||
/// </summary>
|
||||
public class ProfileDescriptor : CorePropertyChanged
|
||||
{
|
||||
internal ProfileDescriptor(ProfileModule profileModule, ProfileEntity profileEntity)
|
||||
{
|
||||
ProfileModule = profileModule;
|
||||
|
||||
Id = profileEntity.Id;
|
||||
Name = profileEntity.Name;
|
||||
IsLastActiveProfile = profileEntity.IsActive;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the module backing the profile
|
||||
/// </summary>
|
||||
public ProfileModule ProfileModule { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique ID of the profile by which it can be loaded from storage
|
||||
/// </summary>
|
||||
public Guid Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the profile
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether this was the last active profile
|
||||
/// </summary>
|
||||
public bool IsLastActiveProfile { get; }
|
||||
}
|
||||
}
|
||||
@@ -11,22 +11,21 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
public abstract class ProfileElement : CorePropertyChanged, IDisposable
|
||||
{
|
||||
private bool _suspended;
|
||||
private Guid _entityId;
|
||||
private string? _name;
|
||||
private int _order;
|
||||
private ProfileElement? _parent;
|
||||
private Profile _profile;
|
||||
private bool _suspended;
|
||||
|
||||
internal List<ProfileElement> ChildrenList;
|
||||
internal bool Disposed;
|
||||
|
||||
internal ProfileElement(Profile profile)
|
||||
{
|
||||
_profile = profile;
|
||||
ChildrenList = new List<ProfileElement>();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique ID of this profile element
|
||||
/// </summary>
|
||||
@@ -95,6 +94,11 @@ namespace Artemis.Core
|
||||
set => SetAndNotify(ref _suspended, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the profile element is disposed
|
||||
/// </summary>
|
||||
public bool Disposed { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates the element
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
public class ProfileConfiguration : CorePropertyChanged, IStorageModel
|
||||
{
|
||||
private ProfileCategory _category;
|
||||
|
||||
private bool _isMissingModule;
|
||||
private bool _isSuspended;
|
||||
private Module? _module;
|
||||
private string _name;
|
||||
private int _order;
|
||||
private Profile? _profile;
|
||||
|
||||
internal ProfileConfiguration(ProfileCategory category, string name, string icon)
|
||||
{
|
||||
_name = name;
|
||||
_category = category;
|
||||
|
||||
Entity = new ProfileConfigurationEntity();
|
||||
Icon = new ProfileConfigurationIcon(Entity) {MaterialIcon = icon};
|
||||
}
|
||||
|
||||
internal ProfileConfiguration(ProfileCategory category, ProfileConfigurationEntity entity)
|
||||
{
|
||||
// Will be loaded from the entity
|
||||
_name = null!;
|
||||
_category = category;
|
||||
|
||||
Entity = entity;
|
||||
Icon = new ProfileConfigurationIcon(Entity);
|
||||
Load();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of this profile configuration
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => SetAndNotify(ref _name, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The order in which this profile appears in the update loop and sidebar
|
||||
/// </summary>
|
||||
public int Order
|
||||
{
|
||||
get => _order;
|
||||
set => SetAndNotify(ref _order, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether this profile is suspended, disabling it regardless of the
|
||||
/// <see cref="ActivationCondition" />
|
||||
/// </summary>
|
||||
public bool IsSuspended
|
||||
{
|
||||
get => _isSuspended;
|
||||
set => SetAndNotify(ref _isSuspended, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether this profile configuration is missing any modules
|
||||
/// </summary>
|
||||
public bool IsMissingModule
|
||||
{
|
||||
get => _isMissingModule;
|
||||
private set => SetAndNotify(ref _isMissingModule, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the category of this profile configuration
|
||||
/// </summary>
|
||||
public ProfileCategory Category
|
||||
{
|
||||
get => _category;
|
||||
internal set => SetAndNotify(ref _category, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the icon configuration
|
||||
/// </summary>
|
||||
public ProfileConfigurationIcon Icon { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the profile of this profile configuration
|
||||
/// </summary>
|
||||
public Profile? Profile
|
||||
{
|
||||
get => _profile;
|
||||
internal set => SetAndNotify(ref _profile, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the behaviour of when this profile is activated
|
||||
/// </summary>
|
||||
public ActivationBehaviour ActivationBehaviour { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data model condition that must evaluate to <see langword="true" /> for this profile to be activated
|
||||
/// alongside any activation requirements of the <see cref="Module" />, if set
|
||||
/// </summary>
|
||||
public DataModelConditionGroup? ActivationCondition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the module this profile uses
|
||||
/// </summary>
|
||||
public Module? Module
|
||||
{
|
||||
get => _module;
|
||||
set
|
||||
{
|
||||
_module = value;
|
||||
IsMissingModule = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the activation conditions where met during the last <see cref="Update" /> call
|
||||
/// </summary>
|
||||
public bool ActivationConditionMet { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether this profile configuration is being edited
|
||||
/// </summary>
|
||||
public bool IsBeingEdited { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the entity used by this profile config
|
||||
/// </summary>
|
||||
public ProfileConfigurationEntity Entity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates this configurations activation condition status
|
||||
/// </summary>
|
||||
public void Update()
|
||||
{
|
||||
ActivationConditionMet = ActivationCondition == null || ActivationCondition.Evaluate();
|
||||
}
|
||||
|
||||
public bool ShouldBeActive(bool includeActivationCondition)
|
||||
{
|
||||
if (Category.IsSuspended || IsSuspended || IsMissingModule)
|
||||
return false;
|
||||
|
||||
if (includeActivationCondition)
|
||||
return ActivationConditionMet && (Module == null || Module.IsActivated);
|
||||
return Module == null || Module.IsActivated;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"[ProfileConfiguration] {nameof(Name)}: {Name}";
|
||||
}
|
||||
|
||||
internal void LoadModules(List<Module> enabledModules)
|
||||
{
|
||||
Module = enabledModules.FirstOrDefault(m => m.Id == Entity.ModuleId);
|
||||
IsMissingModule = Module == null && Entity.ModuleId != null;
|
||||
}
|
||||
|
||||
#region Implementation of IStorageModel
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Load()
|
||||
{
|
||||
Name = Entity.Name;
|
||||
IsSuspended = Entity.IsSuspended;
|
||||
ActivationBehaviour = (ActivationBehaviour) Entity.ActivationBehaviour;
|
||||
|
||||
Icon.Load();
|
||||
|
||||
ActivationCondition = Entity.ActivationCondition != null
|
||||
? new DataModelConditionGroup(null, Entity.ActivationCondition)
|
||||
: null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Save()
|
||||
{
|
||||
Entity.Name = Name;
|
||||
Entity.IsSuspended = IsSuspended;
|
||||
Entity.ActivationBehaviour = (int) ActivationBehaviour;
|
||||
Entity.ProfileCategoryId = Category.Entity.Id;
|
||||
|
||||
Icon.Save();
|
||||
|
||||
if (ActivationCondition != null)
|
||||
{
|
||||
ActivationCondition.Save();
|
||||
Entity.ActivationCondition = ActivationCondition.Entity;
|
||||
}
|
||||
else
|
||||
{
|
||||
Entity.ActivationCondition = null;
|
||||
}
|
||||
|
||||
if (!IsMissingModule)
|
||||
Entity.ModuleId = Module?.Id;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.IO;
|
||||
using Artemis.Core.JsonConverters;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A model that can be used to serialize a profile configuration, it's profile and it's icon
|
||||
/// </summary>
|
||||
public class ProfileConfigurationExportModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the storage entity of the profile configuration
|
||||
/// </summary>
|
||||
public ProfileConfigurationEntity? ProfileConfigurationEntity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the storage entity of the profile
|
||||
/// </summary>
|
||||
[JsonProperty(Required = Required.Always)]
|
||||
public ProfileEntity ProfileEntity { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a stream containing the profile image
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(StreamConverter))]
|
||||
public Stream? ProfileImage { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the icon of a <see cref="ProfileConfiguration" />
|
||||
/// </summary>
|
||||
public class ProfileConfigurationIcon : CorePropertyChanged, IStorageModel
|
||||
{
|
||||
private readonly ProfileConfigurationEntity _entity;
|
||||
private Stream? _fileIcon;
|
||||
private ProfileConfigurationIconType _iconType;
|
||||
private string? _materialIcon;
|
||||
|
||||
internal ProfileConfigurationIcon(ProfileConfigurationEntity entity)
|
||||
{
|
||||
_entity = entity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of icon this profile configuration uses
|
||||
/// </summary>
|
||||
public ProfileConfigurationIconType IconType
|
||||
{
|
||||
get => _iconType;
|
||||
set => SetAndNotify(ref _iconType, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon if it is a Material icon
|
||||
/// </summary>
|
||||
public string? MaterialIcon
|
||||
{
|
||||
get => _materialIcon;
|
||||
set => SetAndNotify(ref _materialIcon, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a stream containing the icon if it is bitmap or SVG
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Stream? FileIcon
|
||||
{
|
||||
get => _fileIcon;
|
||||
set => SetAndNotify(ref _fileIcon, value);
|
||||
}
|
||||
|
||||
#region Implementation of IStorageModel
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Load()
|
||||
{
|
||||
IconType = (ProfileConfigurationIconType) _entity.IconType;
|
||||
MaterialIcon = _entity.MaterialIcon;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Save()
|
||||
{
|
||||
_entity.IconType = (int) IconType;
|
||||
_entity.MaterialIcon = MaterialIcon;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a type of profile icon
|
||||
/// </summary>
|
||||
public enum ProfileConfigurationIconType
|
||||
{
|
||||
/// <summary>
|
||||
/// An icon picked from the Material Design Icons collection
|
||||
/// </summary>
|
||||
[Description("Material Design Icon")] MaterialIcon,
|
||||
|
||||
/// <summary>
|
||||
/// A bitmap image icon
|
||||
/// </summary>
|
||||
[Description("Bitmap Image")] BitmapImage,
|
||||
|
||||
/// <summary>
|
||||
/// An SVG image icon
|
||||
/// </summary>
|
||||
[Description("SVG Image")] SvgImage
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user