1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Merge branch 'development'

This commit is contained in:
Robert 2021-04-12 12:56:02 +02:00
commit dd54a8a54c
26 changed files with 373 additions and 103 deletions

View File

@ -0,0 +1,13 @@
namespace Artemis.Core
{
internal class StringNotNullConditionOperator : ConditionOperator<string>
{
public override string Description => "Is not null";
public override string Icon => "CheckboxMarkedCircleOutline";
public override bool Evaluate(string a)
{
return !string.IsNullOrWhiteSpace(a);
}
}
}

View File

@ -0,0 +1,13 @@
namespace Artemis.Core
{
internal class StringNullConditionOperator : ConditionOperator<string>
{
public override string Description => "Is null";
public override string Icon => "Null";
public override bool Evaluate(string a)
{
return string.IsNullOrWhiteSpace(a);
}
}
}

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using Artemis.Core.LayerEffects; using Artemis.Core.LayerEffects;
using Artemis.Storage.Entities.Profile; using Artemis.Storage.Entities.Profile;
@ -27,7 +28,6 @@ namespace Artemis.Core
Parent = parent ?? throw new ArgumentNullException(nameof(parent)); Parent = parent ?? throw new ArgumentNullException(nameof(parent));
Profile = Parent.Profile; Profile = Parent.Profile;
Name = name; Name = name;
Enabled = true;
Parent.AddChild(this); Parent.AddChild(this);
} }
@ -46,7 +46,7 @@ namespace Artemis.Core
Profile = profile; Profile = profile;
Parent = parent; Parent = parent;
Name = folderEntity.Name; Name = folderEntity.Name;
Enabled = folderEntity.Enabled; Suspended = folderEntity.Suspended;
Order = folderEntity.Order; Order = folderEntity.Order;
Load(); Load();
@ -62,6 +62,9 @@ namespace Artemis.Core
/// </summary> /// </summary>
public FolderEntity FolderEntity { get; internal set; } public FolderEntity FolderEntity { get; internal set; }
/// <inheritdoc />
public override bool ShouldBeEnabled => !Suspended && DisplayConditionMet && !Timeline.IsFinished;
internal override RenderElementEntity RenderElementEntity => FolderEntity; internal override RenderElementEntity RenderElementEntity => FolderEntity;
/// <inheritdoc /> /// <inheritdoc />
@ -81,12 +84,14 @@ namespace Artemis.Core
if (Disposed) if (Disposed)
throw new ObjectDisposedException("Folder"); throw new ObjectDisposedException("Folder");
if (!Enabled)
return;
UpdateDisplayCondition(); UpdateDisplayCondition();
UpdateTimeline(deltaTime); UpdateTimeline(deltaTime);
if (ShouldBeEnabled)
Enable();
else if (Timeline.IsFinished)
Disable();
foreach (ProfileElement child in Children) foreach (ProfileElement child in Children)
child.Update(deltaTime); child.Update(deltaTime);
} }
@ -175,16 +180,16 @@ namespace Artemis.Core
throw new ObjectDisposedException("Folder"); throw new ObjectDisposedException("Folder");
// Ensure the folder is ready // Ensure the folder is ready
if (!Enabled || !Children.Any(c => c.Enabled) || Path == null) if (!Enabled || Path == null)
return; return;
// No point rendering if none of the children are going to render // No point rendering if all children are disabled
if (!Children.Any(c => c is RenderProfileElement renderElement && !renderElement.Timeline.IsFinished)) if (!Children.Any(c => c is RenderProfileElement {Enabled: true}))
return; return;
lock (Timeline) lock (Timeline)
{ {
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled)) foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => !e.Suspended))
{ {
baseLayerEffect.BaseProperties?.Update(Timeline); baseLayerEffect.BaseProperties?.Update(Timeline);
baseLayerEffect.Update(Timeline.Delta.TotalSeconds); baseLayerEffect.Update(Timeline.Delta.TotalSeconds);
@ -194,7 +199,7 @@ namespace Artemis.Core
try try
{ {
SKRectI rendererBounds = SKRectI.Create(0, 0, Bounds.Width, Bounds.Height); SKRectI rendererBounds = SKRectI.Create(0, 0, Bounds.Width, Bounds.Height);
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled)) foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => !e.Suspended))
baseLayerEffect.PreProcess(canvas, rendererBounds, layerPaint); baseLayerEffect.PreProcess(canvas, rendererBounds, layerPaint);
canvas.SaveLayer(layerPaint); canvas.SaveLayer(layerPaint);
@ -215,7 +220,7 @@ namespace Artemis.Core
for (int index = Children.Count - 1; index > -1; index--) for (int index = Children.Count - 1; index > -1; index--)
Children[index].Render(canvas, new SKPointI(Bounds.Left, Bounds.Top)); Children[index].Render(canvas, new SKPointI(Bounds.Left, Bounds.Top));
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled)) foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => !e.Suspended))
baseLayerEffect.PostProcess(canvas, rendererBounds, layerPaint); baseLayerEffect.PostProcess(canvas, rendererBounds, layerPaint);
} }
finally finally
@ -270,7 +275,7 @@ namespace Artemis.Core
FolderEntity.Order = Order; FolderEntity.Order = Order;
FolderEntity.Name = Name; FolderEntity.Name = Name;
FolderEntity.Enabled = Enabled; FolderEntity.Suspended = Suspended;
FolderEntity.ProfileId = Profile.EntityId; FolderEntity.ProfileId = Profile.EntityId;
FolderEntity.ExpandedPropertyGroups.Clear(); FolderEntity.ExpandedPropertyGroups.Clear();
@ -279,6 +284,46 @@ namespace Artemis.Core
SaveRenderElement(); SaveRenderElement();
} }
/// <inheritdoc />
public override void Enable()
{
if (Enabled)
return;
Debug.WriteLine($"Enabling {this}");
foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
baseLayerEffect.InternalEnable();
foreach (ProfileElement profileElement in Children)
{
if (profileElement is RenderProfileElement renderProfileElement && renderProfileElement.ShouldBeEnabled)
renderProfileElement.Enable();
}
Enabled = true;
}
/// <inheritdoc />
public override void Disable()
{
if (!Enabled)
return;
Debug.WriteLine($"Disabled {this}");
foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
baseLayerEffect.InternalDisable();
foreach (ProfileElement profileElement in Children)
{
if (profileElement is RenderProfileElement renderProfileElement)
renderProfileElement.Disable();
}
Enabled = false;
}
#region Events #region Events
/// <summary> /// <summary>

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq; using System.Linq;
using Artemis.Core.LayerBrushes; using Artemis.Core.LayerBrushes;
using Artemis.Core.LayerEffects; using Artemis.Core.LayerEffects;
@ -35,7 +36,7 @@ namespace Artemis.Core
Parent = parent ?? throw new ArgumentNullException(nameof(parent)); Parent = parent ?? throw new ArgumentNullException(nameof(parent));
Profile = Parent.Profile; Profile = Parent.Profile;
Name = name; Name = name;
Enabled = true; Suspended = false;
_general = new LayerGeneralProperties(); _general = new LayerGeneralProperties();
_transform = new LayerTransformProperties(); _transform = new LayerTransformProperties();
@ -120,6 +121,9 @@ namespace Artemis.Core
/// </summary> /// </summary>
public LayerEntity LayerEntity { get; internal set; } public LayerEntity LayerEntity { get; internal set; }
/// <inheritdoc />
public override bool ShouldBeEnabled => !Suspended && DisplayConditionMet;
internal override RenderElementEntity RenderElementEntity => LayerEntity; internal override RenderElementEntity RenderElementEntity => LayerEntity;
/// <inheritdoc /> /// <inheritdoc />
@ -192,7 +196,7 @@ namespace Artemis.Core
{ {
EntityId = LayerEntity.Id; EntityId = LayerEntity.Id;
Name = LayerEntity.Name; Name = LayerEntity.Name;
Enabled = LayerEntity.Enabled; Suspended = LayerEntity.Suspended;
Order = LayerEntity.Order; Order = LayerEntity.Order;
ExpandedPropertyGroups.AddRange(LayerEntity.ExpandedPropertyGroups); ExpandedPropertyGroups.AddRange(LayerEntity.ExpandedPropertyGroups);
@ -208,7 +212,7 @@ namespace Artemis.Core
LayerEntity.Id = EntityId; LayerEntity.Id = EntityId;
LayerEntity.ParentId = Parent?.EntityId ?? new Guid(); LayerEntity.ParentId = Parent?.EntityId ?? new Guid();
LayerEntity.Order = Order; LayerEntity.Order = Order;
LayerEntity.Enabled = Enabled; LayerEntity.Suspended = Suspended;
LayerEntity.Name = Name; LayerEntity.Name = Name;
LayerEntity.ProfileId = Profile.EntityId; LayerEntity.ProfileId = Profile.EntityId;
LayerEntity.ExpandedPropertyGroups.Clear(); LayerEntity.ExpandedPropertyGroups.Clear();
@ -262,11 +266,13 @@ namespace Artemis.Core
if (Disposed) if (Disposed)
throw new ObjectDisposedException("Layer"); throw new ObjectDisposedException("Layer");
if (!Enabled)
return;
UpdateDisplayCondition(); UpdateDisplayCondition();
UpdateTimeline(deltaTime); UpdateTimeline(deltaTime);
if (ShouldBeEnabled)
Enable();
else if (Timeline.IsFinished)
Disable();
} }
/// <inheritdoc /> /// <inheritdoc />
@ -295,6 +301,36 @@ namespace Artemis.Core
Timeline.ClearDelta(); Timeline.ClearDelta();
} }
/// <inheritdoc />
public override void Enable()
{
if (Enabled)
return;
Debug.WriteLine($"Enabling {this}");
LayerBrush?.InternalEnable();
foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
baseLayerEffect.InternalEnable();
Enabled = true;
}
/// <inheritdoc />
public override void Disable()
{
if (!Enabled)
return;
Debug.WriteLine($"Disabling {this}");
LayerBrush?.InternalDisable();
foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
baseLayerEffect.InternalDisable();
Enabled = false;
}
private void ApplyTimeline(Timeline timeline) private void ApplyTimeline(Timeline timeline)
{ {
if (timeline.Delta == TimeSpan.Zero) if (timeline.Delta == TimeSpan.Zero)
@ -308,7 +344,7 @@ namespace Artemis.Core
LayerBrush.Update(timeline.Delta.TotalSeconds); LayerBrush.Update(timeline.Delta.TotalSeconds);
} }
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled)) foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => !e.Suspended))
{ {
baseLayerEffect.BaseProperties?.Update(timeline); baseLayerEffect.BaseProperties?.Update(timeline);
baseLayerEffect.Update(timeline.Delta.TotalSeconds); baseLayerEffect.Update(timeline.Delta.TotalSeconds);
@ -387,7 +423,7 @@ namespace Artemis.Core
if (LayerBrush == null) if (LayerBrush == null)
throw new ArtemisCoreException("The layer is not yet ready for rendering"); throw new ArtemisCoreException("The layer is not yet ready for rendering");
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled)) foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => !e.Suspended))
baseLayerEffect.PreProcess(canvas, bounds, layerPaint); baseLayerEffect.PreProcess(canvas, bounds, layerPaint);
try try
@ -398,7 +434,7 @@ namespace Artemis.Core
canvas.ClipPath(renderPath); canvas.ClipPath(renderPath);
LayerBrush.InternalRender(canvas, bounds, layerPaint); LayerBrush.InternalRender(canvas, bounds, layerPaint);
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled)) foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => !e.Suspended))
baseLayerEffect.PostProcess(canvas, bounds, layerPaint); baseLayerEffect.PostProcess(canvas, bounds, layerPaint);
} }

View File

@ -11,7 +11,7 @@ namespace Artemis.Core
/// </summary> /// </summary>
public abstract class ProfileElement : CorePropertyChanged, IDisposable public abstract class ProfileElement : CorePropertyChanged, IDisposable
{ {
private bool _enabled; private bool _suspended;
private Guid _entityId; private Guid _entityId;
private string? _name; private string? _name;
private int _order; private int _order;
@ -87,12 +87,12 @@ namespace Artemis.Core
} }
/// <summary> /// <summary>
/// Gets or sets the enabled state, if not enabled the element is skipped in render and update /// Gets or sets the suspended state, if suspended the element is skipped in render and update
/// </summary> /// </summary>
public bool Enabled public bool Suspended
{ {
get => _enabled; get => _suspended;
set => SetAndNotify(ref _enabled, value); set => SetAndNotify(ref _suspended, value);
} }
/// <summary> /// <summary>

View File

@ -16,8 +16,8 @@ namespace Artemis.Core
/// </summary> /// </summary>
public abstract class RenderProfileElement : ProfileElement public abstract class RenderProfileElement : ProfileElement
{ {
private SKPath? _path;
private SKRectI _bounds; private SKRectI _bounds;
private SKPath? _path;
internal RenderProfileElement(Profile profile) : base(profile) internal RenderProfileElement(Profile profile) : base(profile)
{ {
@ -29,13 +29,26 @@ namespace Artemis.Core
LayerEffectStore.LayerEffectRemoved += LayerEffectStoreOnLayerEffectRemoved; LayerEffectStore.LayerEffectRemoved += LayerEffectStoreOnLayerEffectRemoved;
} }
/// <summary>
/// Gets a boolean indicating whether this render element and its layers/brushes are enabled
/// </summary>
public bool Enabled { get; protected set; }
/// <summary>
/// Gets a boolean indicating whether this render element and its layers/brushes should be enabled
/// </summary>
public abstract bool ShouldBeEnabled { get; }
/// <summary> /// <summary>
/// Creates a list of all layer properties present on this render element /// Creates a list of all layer properties present on this render element
/// </summary> /// </summary>
/// <returns>A list of all layer properties present on this render element</returns> /// <returns>A list of all layer properties present on this render element</returns>
public abstract List<ILayerProperty> GetAllLayerProperties(); public abstract List<ILayerProperty> GetAllLayerProperties();
#region IDisposable /// <summary>
/// Occurs when a layer effect has been added or removed to this render element
/// </summary>
public event EventHandler? LayerEffectsUpdated;
/// <inheritdoc /> /// <inheritdoc />
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
@ -49,8 +62,6 @@ namespace Artemis.Core
base.Dispose(disposing); base.Dispose(disposing);
} }
#endregion
internal void LoadRenderElement() internal void LoadRenderElement()
{ {
DisplayCondition = RenderElementEntity.DisplayCondition != null DisplayCondition = RenderElementEntity.DisplayCondition != null
@ -75,7 +86,7 @@ namespace Artemis.Core
ProviderId = layerEffect.Descriptor?.PlaceholderFor ?? layerEffect.ProviderId, ProviderId = layerEffect.Descriptor?.PlaceholderFor ?? layerEffect.ProviderId,
EffectType = layerEffect.GetEffectTypeName(), EffectType = layerEffect.GetEffectTypeName(),
Name = layerEffect.Name, Name = layerEffect.Name,
Enabled = layerEffect.Enabled, Suspended = layerEffect.Suspended,
HasBeenRenamed = layerEffect.HasBeenRenamed, HasBeenRenamed = layerEffect.HasBeenRenamed,
Order = layerEffect.Order Order = layerEffect.Order
}; };
@ -92,6 +103,11 @@ namespace Artemis.Core
Timeline?.Save(); Timeline?.Save();
} }
internal void OnLayerEffectsUpdated()
{
LayerEffectsUpdated?.Invoke(this, EventArgs.Empty);
}
#region Timeline #region Timeline
/// <summary> /// <summary>
@ -188,6 +204,20 @@ namespace Artemis.Core
#endregion #endregion
#region State
/// <summary>
/// Enables the render element and its brushes and effects
/// </summary>
public abstract void Disable();
/// <summary>
/// Disables the render element and its brushes and effects
/// </summary>
public abstract void Enable();
#endregion
#region Effect management #region Effect management
internal List<BaseLayerEffect> LayerEffectsList; internal List<BaseLayerEffect> LayerEffectsList;
@ -208,7 +238,7 @@ namespace Artemis.Core
LayerEffectEntity entity = new() LayerEffectEntity entity = new()
{ {
Id = Guid.NewGuid(), Id = Guid.NewGuid(),
Enabled = true, Suspended = false,
Order = LayerEffects.Count + 1 Order = LayerEffects.Count + 1
}; };
descriptor.CreateInstance(this, entity); descriptor.CreateInstance(this, entity);
@ -338,6 +368,12 @@ namespace Artemis.Core
/// </summary> /// </summary>
public void UpdateDisplayCondition() public void UpdateDisplayCondition()
{ {
if (Suspended)
{
DisplayConditionMet = false;
return;
}
if (DisplayCondition == null) if (DisplayCondition == null)
{ {
DisplayConditionMet = true; DisplayConditionMet = true;
@ -381,19 +417,5 @@ namespace Artemis.Core
} }
#endregion #endregion
#region Events
/// <summary>
/// Occurs when a layer effect has been added or removed to this render element
/// </summary>
public event EventHandler? LayerEffectsUpdated;
internal void OnLayerEffectsUpdated()
{
LayerEffectsUpdated?.Invoke(this, EventArgs.Empty);
}
#endregion
} }
} }

View File

@ -82,6 +82,11 @@ namespace Artemis.Core.LayerBrushes
/// </summary> /// </summary>
public virtual ILayerBrushPreset? DefaultPreset => Presets?.FirstOrDefault(); public virtual ILayerBrushPreset? DefaultPreset => Presets?.FirstOrDefault();
/// <summary>
/// Gets a boolean indicating whether the layer brush is enabled or not
/// </summary>
public bool Enabled { get; private set; }
/// <summary> /// <summary>
/// Gets or sets whether the brush supports transformations /// Gets or sets whether the brush supports transformations
/// <para>Note: RGB.NET brushes can never be transformed and setting this to true will throw an exception</para> /// <para>Note: RGB.NET brushes can never be transformed and setting this to true will throw an exception</para>
@ -113,14 +118,6 @@ namespace Artemis.Core.LayerBrushes
/// <param name="deltaTime">Seconds passed since last update</param> /// <param name="deltaTime">Seconds passed since last update</param>
public abstract void Update(double deltaTime); public abstract void Update(double deltaTime);
// Not only is this needed to initialize properties on the layer brushes, it also prevents implementing anything
// but LayerBrush<T> and RgbNetLayerBrush<T> outside the core
internal abstract void Initialize();
internal abstract void InternalRender(SKCanvas canvas, SKRect path, SKPaint paint);
#region IDisposable
/// <summary> /// <summary>
/// Releases the unmanaged resources used by the object and optionally releases the managed resources. /// Releases the unmanaged resources used by the object and optionally releases the managed resources.
/// </summary> /// </summary>
@ -137,14 +134,42 @@ namespace Artemis.Core.LayerBrushes
} }
} }
/// <summary>
/// Enables the layer brush if it isn't already enabled
/// </summary>
internal void InternalEnable()
{
if (Enabled)
return;
EnableLayerBrush();
Enabled = true;
}
/// <summary>
/// Disables the layer brush if it isn't already disabled
/// </summary>
internal void InternalDisable()
{
if (!Enabled)
return;
DisableLayerBrush();
Enabled = false;
}
// Not only is this needed to initialize properties on the layer brushes, it also prevents implementing anything
// but LayerBrush<T> and RgbNetLayerBrush<T> outside the core
internal abstract void Initialize();
internal abstract void InternalRender(SKCanvas canvas, SKRect path, SKPaint paint);
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
Dispose(true); Dispose(true);
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
#endregion
} }
/// <summary> /// <summary>

View File

@ -71,6 +71,9 @@ namespace Artemis.Core.LayerBrushes
layer.LayerBrush = brush; layer.LayerBrush = brush;
layer.OnLayerBrushUpdated(); layer.OnLayerBrushUpdated();
if (layer.ShouldBeEnabled)
brush.InternalEnable();
} }
} }
} }

View File

@ -11,7 +11,7 @@ namespace Artemis.Core.LayerEffects
{ {
private ILayerEffectConfigurationDialog? _configurationDialog; private ILayerEffectConfigurationDialog? _configurationDialog;
private LayerEffectDescriptor _descriptor; private LayerEffectDescriptor _descriptor;
private bool _enabled; private bool _suspended;
private Guid _entityId; private Guid _entityId;
private bool _hasBeenRenamed; private bool _hasBeenRenamed;
private string _name; private string _name;
@ -55,12 +55,12 @@ namespace Artemis.Core.LayerEffects
} }
/// <summary> /// <summary>
/// Gets or sets the enabled state, if not enabled the effect is skipped in render and update /// Gets or sets the suspended state, if suspended the effect is skipped in render and update
/// </summary> /// </summary>
public bool Enabled public bool Suspended
{ {
get => _enabled; get => _suspended;
set => SetAndNotify(ref _enabled, value); set => SetAndNotify(ref _suspended, value);
} }
/// <summary> /// <summary>
@ -112,6 +112,11 @@ namespace Artemis.Core.LayerEffects
internal string PropertyRootPath => $"LayerEffect.{EntityId}.{GetType().Name}."; internal string PropertyRootPath => $"LayerEffect.{EntityId}.{GetType().Name}.";
/// <summary>
/// Gets a boolean indicating whether the layer effect is enabled or not
/// </summary>
public bool Enabled { get; private set; }
#region IDisposable #region IDisposable
/// <summary> /// <summary>
@ -176,5 +181,29 @@ namespace Artemis.Core.LayerEffects
internal abstract void Initialize(); internal abstract void Initialize();
internal virtual string GetEffectTypeName() => GetType().Name; internal virtual string GetEffectTypeName() => GetType().Name;
/// <summary>
/// Enables the layer effect if it isn't already enabled
/// </summary>
internal void InternalEnable()
{
if (Enabled)
return;
EnableLayerEffect();
Enabled = true;
}
/// <summary>
/// Disables the layer effect if it isn't already disabled
/// </summary>
internal void InternalDisable()
{
if (!Enabled)
return;
DisableLayerEffect();
Enabled = false;
}
} }
} }

View File

@ -74,7 +74,7 @@ namespace Artemis.Core.LayerEffects
effect.EntityId = entity.Id; effect.EntityId = entity.Id;
effect.Order = entity.Order; effect.Order = entity.Order;
effect.Name = entity.Name; effect.Name = entity.Name;
effect.Enabled = entity.Enabled; effect.Suspended = entity.Suspended;
effect.Descriptor = this; effect.Descriptor = this;
effect.Initialize(); effect.Initialize();
@ -94,6 +94,9 @@ namespace Artemis.Core.LayerEffects
}; };
effect.Initialize(); effect.Initialize();
renderElement.ActivateLayerEffect(effect); renderElement.ActivateLayerEffect(effect);
if (renderElement.ShouldBeEnabled)
effect.InternalEnable();
} }
} }
} }

View File

@ -16,7 +16,7 @@ namespace Artemis.Core.LayerEffects.Placeholder
EntityId = OriginalEntity.Id; EntityId = OriginalEntity.Id;
Order = OriginalEntity.Order; Order = OriginalEntity.Order;
Name = OriginalEntity.Name; Name = OriginalEntity.Name;
Enabled = OriginalEntity.Enabled; Suspended = OriginalEntity.Suspended;
HasBeenRenamed = OriginalEntity.HasBeenRenamed; HasBeenRenamed = OriginalEntity.HasBeenRenamed;
} }

View File

@ -98,11 +98,19 @@ namespace Artemis.Core.Services
} }
else else
{ {
PluginInfo pluginInfo;
try try
{ {
// Compare versions, copy if the same when debugging // Compare versions, copy if the same when debugging
PluginInfo pluginInfo = CoreJson.DeserializeObject<PluginInfo>(File.ReadAllText(metadataFile))!; pluginInfo = CoreJson.DeserializeObject<PluginInfo>(File.ReadAllText(metadataFile))!;
}
catch (Exception e)
{
throw new ArtemisPluginException($"Failed read plugin metadata needed to install built-in plugin: {e.Message}", e);
}
try
{
if (builtInPluginInfo.Version > pluginInfo.Version) if (builtInPluginInfo.Version > pluginInfo.Version)
{ {
_logger.Debug("Copying updated built-in plugin from {pluginInfo} to {builtInPluginInfo}", pluginInfo, builtInPluginInfo); _logger.Debug("Copying updated built-in plugin from {pluginInfo} to {builtInPluginInfo}", pluginInfo, builtInPluginInfo);
@ -111,7 +119,7 @@ namespace Artemis.Core.Services
} }
catch (Exception e) catch (Exception e)
{ {
throw new ArtemisPluginException("Failed read plugin metadata needed to install built-in plugin", e); throw new ArtemisPluginException($"Failed to install built-in plugin: {e.Message}", e);
} }
} }
} }

View File

@ -61,6 +61,8 @@ namespace Artemis.Core.Services
RegisterConditionOperator(Constants.CorePlugin, new StringStartsWithConditionOperator()); RegisterConditionOperator(Constants.CorePlugin, new StringStartsWithConditionOperator());
RegisterConditionOperator(Constants.CorePlugin, new StringEndsWithConditionOperator()); RegisterConditionOperator(Constants.CorePlugin, new StringEndsWithConditionOperator());
RegisterConditionOperator(Constants.CorePlugin, new StringMatchesRegexConditionOperator()); RegisterConditionOperator(Constants.CorePlugin, new StringMatchesRegexConditionOperator());
RegisterConditionOperator(Constants.CorePlugin, new StringNullConditionOperator());
RegisterConditionOperator(Constants.CorePlugin, new StringNotNullConditionOperator());
// Null checks, at the bottom // Null checks, at the bottom
// TODO: Implement a priority mechanism // TODO: Implement a priority mechanism

View File

@ -16,7 +16,7 @@ namespace Artemis.Storage.Entities.Profile
public int Order { get; set; } public int Order { get; set; }
public string Name { get; set; } public string Name { get; set; }
public bool Enabled { get; set; } public bool Suspended { get; set; }
[BsonRef("ProfileEntity")] [BsonRef("ProfileEntity")]
public ProfileEntity Profile { get; set; } public ProfileEntity Profile { get; set; }

View File

@ -8,7 +8,7 @@ namespace Artemis.Storage.Entities.Profile
public string ProviderId { get; set; } public string ProviderId { get; set; }
public string EffectType { get; set; } public string EffectType { get; set; }
public string Name { get; set; } public string Name { get; set; }
public bool Enabled { get; set; } public bool Suspended { get; set; }
public bool HasBeenRenamed { get; set; } public bool HasBeenRenamed { get; set; }
public int Order { get; set; } public int Order { get; set; }
} }

View File

@ -17,7 +17,7 @@ namespace Artemis.Storage.Entities.Profile
public int Order { get; set; } public int Order { get; set; }
public string Name { get; set; } public string Name { get; set; }
public bool Enabled { get; set; } public bool Suspended { get; set; }
public List<LedEntity> Leds { get; set; } public List<LedEntity> Leds { get; set; }

View File

@ -16,16 +16,16 @@ namespace Artemis.Storage.Migrations
{ {
foreach (FolderEntity profileEntityFolder in profileEntity.Folders) foreach (FolderEntity profileEntityFolder in profileEntity.Folders)
{ {
profileEntityFolder.Enabled = true; profileEntityFolder.Suspended = false;
foreach (LayerEffectEntity layerEffectEntity in profileEntityFolder.LayerEffects) foreach (LayerEffectEntity layerEffectEntity in profileEntityFolder.LayerEffects)
layerEffectEntity.Enabled = true; layerEffectEntity.Suspended = false;
} }
foreach (LayerEntity profileEntityLayer in profileEntity.Layers) foreach (LayerEntity profileEntityLayer in profileEntity.Layers)
{ {
profileEntityLayer.Enabled = true; profileEntityLayer.Suspended = false;
foreach (LayerEffectEntity layerEffectEntity in profileEntityLayer.LayerEffects) foreach (LayerEffectEntity layerEffectEntity in profileEntityLayer.LayerEffects)
layerEffectEntity.Enabled = true; layerEffectEntity.Suspended = false;
} }
repository.Upsert(profileEntity); repository.Upsert(profileEntity);

View File

@ -124,15 +124,30 @@ namespace Artemis.UI.Shared.Services
if (SelectedProfile == null || _doTick) if (SelectedProfile == null || _doTick)
return; return;
// Stick to the main segment for any element that is not currently selected TickProfileElement(SelectedProfile.GetRootFolder());
foreach (Folder folder in SelectedProfile.GetAllFolders())
folder.Timeline.Override(CurrentTime, folder.Timeline.PlayMode == TimelinePlayMode.Repeat);
foreach (Layer layer in SelectedProfile.GetAllLayers())
layer.Timeline.Override(CurrentTime, (layer != SelectedProfileElement || layer.Timeline.Length < CurrentTime) && layer.Timeline.PlayMode == TimelinePlayMode.Repeat);
_doTick = true; _doTick = true;
} }
private void TickProfileElement(ProfileElement profileElement)
{
if (profileElement is not RenderProfileElement renderElement)
return;
if (renderElement.Suspended)
renderElement.Disable();
else
{
renderElement.Enable();
renderElement.Timeline.Override(
CurrentTime,
(renderElement != SelectedProfileElement || renderElement.Timeline.Length < CurrentTime) && renderElement.Timeline.PlayMode == TimelinePlayMode.Repeat
);
foreach (ProfileElement child in renderElement.Children)
TickProfileElement(child);
}
}
private void SelectedProfileOnDeactivated(object? sender, EventArgs e) private void SelectedProfileOnDeactivated(object? sender, EventArgs e)
{ {
// Execute.PostToUIThread(() => ChangeSelectedProfile(null)); // Execute.PostToUIThread(() => ChangeSelectedProfile(null));

View File

@ -4,13 +4,16 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Reflection;
using System.Security.Principal; using System.Security.Principal;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Screens;
using Artemis.UI.Utilities; using Artemis.UI.Utilities;
using Ninject; using Ninject;
using Ookii.Dialogs.Wpf;
using Stylet; using Stylet;
namespace Artemis.UI namespace Artemis.UI
@ -127,5 +130,48 @@ namespace Artemis.UI
Execute.OnUIThread(() => Application.Current.Shutdown()); Execute.OnUIThread(() => Application.Current.Shutdown());
} }
public void DisplayException(Exception e)
{
using TaskDialog dialog = new();
AssemblyInformationalVersionAttribute versionAttribute = typeof(ApplicationStateManager).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
dialog.WindowTitle = $"Artemis {versionAttribute?.InformationalVersion} build {Constants.BuildInfo.BuildNumberDisplay}";
dialog.MainInstruction = "Unfortunately Artemis ran into an unhandled exception and cannot continue.";
dialog.Content = e.Message;
dialog.ExpandedInformation = e.StackTrace.Trim();
dialog.CollapsedControlText = "Show stack trace";
dialog.ExpandedControlText = "Hide stack trace";
dialog.Footer = "If this keeps happening check out the <a href=\"https://wiki.artemis-rgb.com\">wiki</a> or hit us up on <a href=\"https://discord.gg/S3MVaC9\">Discord</a>.";
dialog.FooterIcon = TaskDialogIcon.Error;
dialog.EnableHyperlinks = true;
dialog.HyperlinkClicked += OpenHyperlink;
TaskDialogButton copyButton = new("Copy stack trace");
TaskDialogButton closeButton = new("Close") {Default = true};
dialog.Buttons.Add(copyButton);
dialog.Buttons.Add(closeButton);
dialog.ButtonClicked += (_, args) =>
{
if (args.Item == copyButton)
{
Clipboard.SetText(e.ToString());
args.Cancel = true;
}
};
dialog.ShowDialog(Application.Current.MainWindow);
}
private void OpenHyperlink(object sender, HyperlinkClickedEventArgs e)
{
ProcessStartInfo processInfo = new()
{
FileName = e.Href,
UseShellExecute = true
};
Process.Start(processInfo);
}
} }
} }

View File

@ -48,7 +48,14 @@ namespace Artemis.UI
return; return;
} }
try { DPIAwareness.Initalize(); } catch (Exception ex) { logger.Error($"Failed to set DPI-Awareness: {ex.Message}"); } try
{
DPIAwareness.Initalize();
}
catch (Exception ex)
{
logger.Error($"Failed to set DPI-Awareness: {ex.Message}");
}
IViewManager viewManager = Kernel.Get<IViewManager>(); IViewManager viewManager = Kernel.Get<IViewManager>();
StartupArguments = Args.ToList(); StartupArguments = Args.ToList();
@ -71,7 +78,7 @@ namespace Artemis.UI
Execute.OnUIThreadSync(() => Execute.OnUIThreadSync(() =>
{ {
UIElement view = viewManager.CreateAndBindViewForModelIfNecessary(RootViewModel); UIElement view = viewManager.CreateAndBindViewForModelIfNecessary(RootViewModel);
((TrayViewModel)RootViewModel).SetTaskbarIcon(view); ((TrayViewModel) RootViewModel).SetTaskbarIcon(view);
}); });
// Initialize the core async so the UI can show the progress // Initialize the core async so the UI can show the progress
@ -136,15 +143,9 @@ namespace Artemis.UI
private void HandleFatalException(Exception e, ILogger logger) private void HandleFatalException(Exception e, ILogger logger)
{ {
logger.Fatal(e, "Fatal exception during initialization, shutting down."); logger.Fatal(e, "Fatal exception during initialization, shutting down.");
// Can't use a pretty exception dialog here since the UI might not even be visible
Execute.OnUIThread(() => Execute.OnUIThread(() =>
{ {
Kernel.Get<IWindowManager>().ShowMessageBox(e.Message + "\n\n Please refer the log file for more details.", _applicationStateManager.DisplayException(e);
"Fatal exception during initialization",
MessageBoxButton.OK,
MessageBoxImage.Error
);
Environment.Exit(1); Environment.Exit(1);
}); });
} }

View File

@ -13,6 +13,7 @@
d:DataContext="{d:DesignInstance local:TreeGroupViewModel}" d:DataContext="{d:DesignInstance local:TreeGroupViewModel}"
d:DesignHeight="450" d:DesignWidth="800"> d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources> <UserControl.Resources>
<converters:InverseBooleanConverter x:Key="InverseBooleanConverter"/>
<converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" /> <converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<converters:PropertyTreeMarginConverter Length="20" x:Key="PropertyTreeMarginConverter" /> <converters:PropertyTreeMarginConverter Length="20" x:Key="PropertyTreeMarginConverter" />
</UserControl.Resources> </UserControl.Resources>
@ -180,13 +181,13 @@
<StackPanel Grid.Column="5" Orientation="Horizontal"> <StackPanel Grid.Column="5" Orientation="Horizontal">
<ToggleButton <ToggleButton
Style="{StaticResource MaterialDesignFlatToggleButton}" Style="{StaticResource MaterialDesignFlatToggleButton}"
ToolTip="Toggle enabled state" ToolTip="Toggle suspended state"
Width="18" Width="18"
Height="18" Height="18"
IsChecked="{Binding LayerPropertyGroup.LayerEffect.Enabled}" IsChecked="{Binding LayerPropertyGroup.LayerEffect.Suspended, Converter={StaticResource InverseBooleanConverter}}"
VerticalAlignment="Center" Padding="-25" VerticalAlignment="Center" Padding="-25"
Margin="5 0" Margin="5 0"
Command="{s:Action EnableToggled}"> Command="{s:Action SuspendedToggled}">
<materialDesign:PackIcon Kind="Eye" Height="13" Width="13" /> <materialDesign:PackIcon Kind="Eye" Height="13" Width="13" />
</ToggleButton> </ToggleButton>
<Button Style="{StaticResource MaterialDesignIconButton}" <Button Style="{StaticResource MaterialDesignIconButton}"

View File

@ -145,7 +145,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
_profileEditorService.UpdateSelectedProfile(); _profileEditorService.UpdateSelectedProfile();
} }
public void EnableToggled() public void SuspendedToggled()
{ {
_profileEditorService.UpdateSelectedProfile(); _profileEditorService.UpdateSelectedProfile();
} }

View File

@ -1,4 +1,4 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem.FolderView" <UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@ -6,9 +6,13 @@
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:treeItem1="clr-namespace:Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem" xmlns:treeItem1="clr-namespace:Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem"
xmlns:Converters="clr-namespace:Artemis.UI.Converters" x:Class="Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem.FolderView"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance {x:Type treeItem1:FolderViewModel}}"> d:DataContext="{d:DesignInstance {x:Type treeItem1:FolderViewModel}}">
<UserControl.Resources>
<Converters:InverseBooleanConverter x:Key="InverseBooleanConverter"/>
</UserControl.Resources>
<!-- Capture clicks on full tree view item --> <!-- Capture clicks on full tree view item -->
<StackPanel Margin="-10" Background="Transparent" ContextMenuOpening="{s:Action ContextMenuOpening}"> <StackPanel Margin="-10" Background="Transparent" ContextMenuOpening="{s:Action ContextMenuOpening}">
<StackPanel.ContextMenu> <StackPanel.ContextMenu>
@ -67,11 +71,11 @@
<TextBlock Grid.Column="1" Text="{Binding ProfileElement.Name}" Margin="10 0 0 0" VerticalAlignment="Center" /> <TextBlock Grid.Column="1" Text="{Binding ProfileElement.Name}" Margin="10 0 0 0" VerticalAlignment="Center" />
<ToggleButton Grid.Column="2" <ToggleButton Grid.Column="2"
Style="{StaticResource MaterialDesignFlatToggleButton}" Style="{StaticResource MaterialDesignFlatToggleButton}"
ToolTip="Toggle enabled state" ToolTip="Toggle suspended state"
Width="18" Width="18"
Height="18" Height="18"
IsChecked="{Binding ProfileElement.Enabled}" IsChecked="{Binding ProfileElement.Suspended, Converter={StaticResource InverseBooleanConverter}}"
Command="{s:Action EnableToggled}" Command="{s:Action SuspendedToggled}"
VerticalAlignment="Center" Padding="-25"> VerticalAlignment="Center" Padding="-25">
<materialDesign:PackIcon Kind="Eye" Height="13" Width="13" /> <materialDesign:PackIcon Kind="Eye" Height="13" Width="13" />
</ToggleButton> </ToggleButton>

View File

@ -1,4 +1,4 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem.LayerView" <UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@ -6,9 +6,13 @@
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:treeItem1="clr-namespace:Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem" xmlns:treeItem1="clr-namespace:Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem"
xmlns:Converters="clr-namespace:Artemis.UI.Converters" x:Class="Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem.LayerView"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance {x:Type treeItem1:LayerViewModel}}"> d:DataContext="{d:DesignInstance {x:Type treeItem1:LayerViewModel}}">
<UserControl.Resources>
<Converters:InverseBooleanConverter x:Key="InverseBooleanConverter"/>
</UserControl.Resources>
<!-- Capture clicks on full tree view item --> <!-- Capture clicks on full tree view item -->
<StackPanel Margin="-10" Background="Transparent" ContextMenuOpening="{s:Action ContextMenuOpening}"> <StackPanel Margin="-10" Background="Transparent" ContextMenuOpening="{s:Action ContextMenuOpening}">
<StackPanel.ContextMenu> <StackPanel.ContextMenu>
@ -60,11 +64,11 @@
<TextBlock Grid.Column="2" Text="{Binding Layer.Name}" VerticalAlignment="Center" Margin="5 0 0 0" /> <TextBlock Grid.Column="2" Text="{Binding Layer.Name}" VerticalAlignment="Center" Margin="5 0 0 0" />
<ToggleButton Grid.Column="3" <ToggleButton Grid.Column="3"
Style="{StaticResource MaterialDesignFlatToggleButton}" Style="{StaticResource MaterialDesignFlatToggleButton}"
ToolTip="Toggle enabled state" ToolTip="Toggle suspended state"
Width="18" Width="18"
Height="18" Height="18"
IsChecked="{Binding ProfileElement.Enabled}" IsChecked="{Binding ProfileElement.Suspended, Converter={StaticResource InverseBooleanConverter}}"
Command="{s:Action EnableToggled}" Command="{s:Action SuspendedToggled}"
VerticalAlignment="Center" Padding="-25"> VerticalAlignment="Center" Padding="-25">
<materialDesign:PackIcon Kind="Eye" Height="13" Width="13" /> <materialDesign:PackIcon Kind="Eye" Height="13" Width="13" />
</ToggleButton> </ToggleButton>

View File

@ -241,7 +241,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
((BindableCollection<TreeItemViewModel>) Items).Sort(i => i.ProfileElement.Order); ((BindableCollection<TreeItemViewModel>) Items).Sort(i => i.ProfileElement.Order);
} }
public void EnableToggled() public void SuspendedToggled()
{ {
_profileEditorService.UpdateSelectedProfile(); _profileEditorService.UpdateSelectedProfile();
} }