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:
Robert Beekman authored and GitHub committed 2021-06-03 22:34:43 +02:00
1 parent 4152c290d2
commit ceeaa4bf6d
164 files changed
+5019 -4220

No files matched your search

@@ -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>
@@ -16,7 +16,7 @@ namespace Artemis.Core
{
internal EventPredicateWrapperDataModel()
{
Feature = Constants.CorePluginFeature;
Module = Constants.CorePluginFeature;
}
/// <summary>
@@ -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;
+1 -7
View File
@@ -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();
+2
View File
@@ -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
+10 -79
View File
@@ -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
}
}