1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-12 21:38:38 +00:00

Layers - Prevent brushes from going null when provider is missing

Layers - Set broken state when brush provider is missing
UI - Tweaked broken state visuals
This commit is contained in:
Robert 2023-04-14 11:56:51 +02:00
parent 2f270c9c03
commit 3b04447ff7
8 changed files with 83 additions and 49 deletions

View File

@ -59,7 +59,7 @@ public abstract class BreakableModel : CorePropertyChanged, IBreakableModel
}
/// <inheritdoc />
public void SetBrokenState(string state, Exception? exception)
public void SetBrokenState(string state, Exception? exception = null)
{
BrokenState = state ?? throw new ArgumentNullException(nameof(state));
BrokenStateException = exception;

View File

@ -16,6 +16,9 @@ namespace Artemis.Core;
/// </summary>
public sealed class Layer : RenderProfileElement
{
private const string BROKEN_STATE_BRUSH_NOT_FOUND = "Failed to load layer brush, ensure the plugin is enabled";
private const string BROKEN_STATE_INIT_FAILED = "Failed to initialize layer brush";
private readonly List<Layer> _renderCopies = new();
private LayerGeneralProperties _general = new();
private LayerTransformProperties _transform = new();
@ -261,7 +264,10 @@ public sealed class Layer : RenderProfileElement
private void LayerBrushStoreOnLayerBrushRemoved(object? sender, LayerBrushStoreEvent e)
{
if (LayerBrush?.Descriptor == e.Registration.LayerBrushDescriptor)
{
DeactivateLayerBrush();
SetBrokenState(BROKEN_STATE_BRUSH_NOT_FOUND);
}
}
private void LayerBrushStoreOnLayerBrushAdded(object? sender, LayerBrushStoreEvent e)
@ -807,33 +813,46 @@ public sealed class Layer : RenderProfileElement
/// <summary>
/// Changes the current layer brush to the provided layer brush and activates it
/// </summary>
public void ChangeLayerBrush(BaseLayerBrush? layerBrush)
public void ChangeLayerBrush(BaseLayerBrush layerBrush)
{
BaseLayerBrush? oldLayerBrush = LayerBrush;
if (layerBrush == null)
throw new ArgumentNullException(nameof(layerBrush));
General.BrushReference.SetCurrentValue(layerBrush != null ? new LayerBrushReference(layerBrush.Descriptor) : null);
BaseLayerBrush? oldLayerBrush = LayerBrush;
General.BrushReference.SetCurrentValue(new LayerBrushReference(layerBrush.Descriptor));
LayerBrush = layerBrush;
// Don't dispose the brush, only disable it
// That way an undo-action does not need to worry about disposed brushes
oldLayerBrush?.InternalDisable();
if (LayerBrush != null)
ActivateLayerBrush();
else
OnLayerBrushUpdated();
ActivateLayerBrush();
}
internal void ActivateLayerBrush()
private void ActivateLayerBrush()
{
try
{
// If the brush is null, try to instantiate it
if (LayerBrush == null)
{
// If the brush is null, try to instantiate it
// Ensure the provider is loaded and still provides the expected brush
LayerBrushReference? brushReference = General.BrushReference.CurrentValue;
if (brushReference?.LayerBrushProviderId != null && brushReference.BrushType != null)
ChangeLayerBrush(LayerBrushStore.Get(brushReference.LayerBrushProviderId, brushReference.BrushType)?.LayerBrushDescriptor.CreateInstance(this, LayerEntity.LayerBrush));
// If that's not possible there's nothing to do
return;
{
// Only apply the brush if it is successfully retrieved from the store and instantiated
BaseLayerBrush? layerBrush = LayerBrushStore.Get(brushReference.LayerBrushProviderId, brushReference.BrushType)?.LayerBrushDescriptor.CreateInstance(this, LayerEntity.LayerBrush);
if (layerBrush != null)
{
ClearBrokenState(BROKEN_STATE_BRUSH_NOT_FOUND);
ChangeLayerBrush(layerBrush);
}
// If that's not possible there's nothing to do
else
{
SetBrokenState(BROKEN_STATE_BRUSH_NOT_FOUND);
return;
}
}
}
General.ShapeType.IsHidden = LayerBrush != null && !LayerBrush.SupportsTransformation;
@ -846,15 +865,15 @@ public sealed class Layer : RenderProfileElement
}
OnLayerBrushUpdated();
ClearBrokenState("Failed to initialize layer brush");
ClearBrokenState(BROKEN_STATE_INIT_FAILED);
}
catch (Exception e)
{
SetBrokenState("Failed to initialize layer brush", e);
SetBrokenState(BROKEN_STATE_INIT_FAILED, e);
}
}
internal void DeactivateLayerBrush()
private void DeactivateLayerBrush()
{
if (LayerBrush == null)
return;

View File

@ -54,7 +54,8 @@ public class ChangeLayerBrush : IProfileEditorCommand, IDisposable
/// <inheritdoc />
public void Undo()
{
_layer.ChangeLayerBrush(_previousBrush);
if (_previousBrush != null)
_layer.ChangeLayerBrush(_previousBrush);
_executed = false;
}

View File

@ -21,6 +21,7 @@
<!-- Custom styles -->
<StyleInclude Source="/Styles/Border.axaml" />
<StyleInclude Source="/Styles/BrokenState.axaml" />
<StyleInclude Source="/Styles/Skeleton.axaml" />
<StyleInclude Source="/Styles/Button.axaml" />
<StyleInclude Source="/Styles/Condensed.axaml" />

View File

@ -0,0 +1,20 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia">
<Design.PreviewWith>
<controls:HyperlinkButton Grid.Column="0" Classes="icon-button icon-button-small broken-state-button" Margin="50">
<avalonia:MaterialIcon Kind="AlertCircle" />
</controls:HyperlinkButton>
</Design.PreviewWith>
<!-- Add Styles Here -->
<Style Selector="controls|HyperlinkButton.broken-state-button avalonia|MaterialIcon">
<Setter Property="Foreground" Value="#E74C4C" />
</Style>
<Style Selector="controls|HyperlinkButton.broken-state-button:pointerover avalonia|MaterialIcon">
<Setter Property="Foreground" Value="#B93F3F" />
</Style>
</Styles>

View File

@ -4,21 +4,19 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:profileTree="clr-namespace:Artemis.UI.Screens.ProfileEditor.ProfileTree"
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.ProfileTree.FolderTreeItemView"
x:DataType="profileTree:FolderTreeItemViewModel">
<Grid ColumnDefinitions="Auto,Auto,*,Auto,Auto">
<Button Grid.Column="0"
ToolTip.Tip="{CompiledBinding ProfileElement.BrokenState}"
IsVisible="{CompiledBinding ProfileElement.BrokenState, Converter={x:Static ObjectConverters.IsNotNull}}"
Classes="icon-button icon-button-small"
Foreground="White"
Background="#E74C4C"
BorderBrush="#E74C4C"
Margin="0 0 5 0"
Command="{Binding ShowBrokenStateExceptions}">
<controls:HyperlinkButton Grid.Column="0"
Classes="icon-button icon-button-small broken-state-button"
Margin="0 0 5 0"
Command="{CompiledBinding ShowBrokenStateExceptions}"
IsVisible="{CompiledBinding ProfileElement.BrokenState, Converter={x:Static ObjectConverters.IsNotNull}}"
ToolTip.Tip="{CompiledBinding ProfileElement.BrokenState, FallbackValue=''}">
<avalonia:MaterialIcon Kind="AlertCircle" />
</Button>
</controls:HyperlinkButton>
<avalonia:MaterialIcon Grid.Column="1"
Kind="Folder"
Margin="0 0 5 0"

View File

@ -4,19 +4,19 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:profileTree="clr-namespace:Artemis.UI.Screens.ProfileEditor.ProfileTree"
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.ProfileTree.LayerTreeItemView"
x:DataType="profileTree:LayerTreeItemViewModel">
<Grid ColumnDefinitions="Auto,Auto,*,Auto,Auto">
<Button Grid.Column="0"
ToolTip.Tip="{CompiledBinding ProfileElement.BrokenState}"
IsVisible="{CompiledBinding ProfileElement.BrokenState, Converter={x:Static ObjectConverters.IsNotNull}}"
Classes="icon-button icon-button-small"
Foreground="#E74C4C"
Margin="0 0 5 0"
Command="{Binding ShowBrokenStateExceptions}">
<controls:HyperlinkButton Grid.Column="0"
Classes="icon-button icon-button-small broken-state-button"
Margin="0 0 5 0"
Command="{CompiledBinding ShowBrokenStateExceptions}"
IsVisible="{CompiledBinding ProfileElement.BrokenState, Converter={x:Static ObjectConverters.IsNotNull}}"
ToolTip.Tip="{CompiledBinding ProfileElement.BrokenState, FallbackValue=''}">
<avalonia:MaterialIcon Kind="AlertCircle" />
</Button>
</controls:HyperlinkButton>
<avalonia:MaterialIcon Grid.Column="1" Kind="{CompiledBinding Layer.LayerBrush.Descriptor.Icon, FallbackValue=Layers}" Margin="0 0 5 0" />
<TextBox Grid.Column="2"
Margin="-5 0 0 0"

View File

@ -39,21 +39,16 @@
Background="{DynamicResource ContentDialogBackground}">
<Border Background="{DynamicResource TaskDialogHeaderBackground}">
<Grid Classes="node-header" VerticalAlignment="Top" ColumnDefinitions="Auto,*,Auto,Auto">
<Button Grid.Column="0"
VerticalAlignment="Center"
IsVisible="{CompiledBinding Node.BrokenState, Converter={x:Static ObjectConverters.IsNotNull}}"
Classes="icon-button icon-button-small"
Foreground="White"
Background="#E74C4C"
BorderBrush="#E74C4C"
Margin="5 0 0 0"
ToolTip.Tip="{CompiledBinding Node.BrokenState}"
Command="{CompiledBinding ShowBrokenState}">
<avalonia:MaterialIcon Kind="AlertCircle"></avalonia:MaterialIcon>
</Button>
<controls:HyperlinkButton Grid.Column="0"
Classes="icon-button icon-button-small broken-state-button"
Margin="5 0 0 0"
Command="{CompiledBinding ShowBrokenState}"
IsVisible="{CompiledBinding Node.BrokenState, Converter={x:Static ObjectConverters.IsNotNull}}"
ToolTip.Tip="{CompiledBinding Node.BrokenState}">
<avalonia:MaterialIcon Kind="AlertCircle" />
</controls:HyperlinkButton>
<TextBlock Grid.Column="1" VerticalAlignment="Center" Margin="10 0 0 0" Text="{CompiledBinding Node.Name}" ToolTip.Tip="{CompiledBinding Node.Description}" />
<controls:HyperlinkButton Grid.Column="2"
IsVisible="{CompiledBinding Node.HelpUrl, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"