mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Merge pull request #779 from Artemis-RGB/bugfix/layer-brush-null
Layers - Prevent brushes from going null when provider is missing
This commit is contained in:
commit
bee96166dd
@ -59,7 +59,7 @@ public abstract class BreakableModel : CorePropertyChanged, IBreakableModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void SetBrokenState(string state, Exception? exception)
|
public void SetBrokenState(string state, Exception? exception = null)
|
||||||
{
|
{
|
||||||
BrokenState = state ?? throw new ArgumentNullException(nameof(state));
|
BrokenState = state ?? throw new ArgumentNullException(nameof(state));
|
||||||
BrokenStateException = exception;
|
BrokenStateException = exception;
|
||||||
|
|||||||
@ -16,6 +16,9 @@ namespace Artemis.Core;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class Layer : RenderProfileElement
|
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 readonly List<Layer> _renderCopies = new();
|
||||||
private LayerGeneralProperties _general = new();
|
private LayerGeneralProperties _general = new();
|
||||||
private LayerTransformProperties _transform = new();
|
private LayerTransformProperties _transform = new();
|
||||||
@ -261,7 +264,10 @@ public sealed class Layer : RenderProfileElement
|
|||||||
private void LayerBrushStoreOnLayerBrushRemoved(object? sender, LayerBrushStoreEvent e)
|
private void LayerBrushStoreOnLayerBrushRemoved(object? sender, LayerBrushStoreEvent e)
|
||||||
{
|
{
|
||||||
if (LayerBrush?.Descriptor == e.Registration.LayerBrushDescriptor)
|
if (LayerBrush?.Descriptor == e.Registration.LayerBrushDescriptor)
|
||||||
|
{
|
||||||
DeactivateLayerBrush();
|
DeactivateLayerBrush();
|
||||||
|
SetBrokenState(BROKEN_STATE_BRUSH_NOT_FOUND);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LayerBrushStoreOnLayerBrushAdded(object? sender, LayerBrushStoreEvent e)
|
private void LayerBrushStoreOnLayerBrushAdded(object? sender, LayerBrushStoreEvent e)
|
||||||
@ -807,33 +813,46 @@ public sealed class Layer : RenderProfileElement
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Changes the current layer brush to the provided layer brush and activates it
|
/// Changes the current layer brush to the provided layer brush and activates it
|
||||||
/// </summary>
|
/// </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;
|
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();
|
oldLayerBrush?.InternalDisable();
|
||||||
|
ActivateLayerBrush();
|
||||||
if (LayerBrush != null)
|
|
||||||
ActivateLayerBrush();
|
|
||||||
else
|
|
||||||
OnLayerBrushUpdated();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void ActivateLayerBrush()
|
private void ActivateLayerBrush()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// If the brush is null, try to instantiate it
|
||||||
if (LayerBrush == null)
|
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;
|
LayerBrushReference? brushReference = General.BrushReference.CurrentValue;
|
||||||
if (brushReference?.LayerBrushProviderId != null && brushReference.BrushType != null)
|
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
|
// Only apply the brush if it is successfully retrieved from the store and instantiated
|
||||||
return;
|
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;
|
General.ShapeType.IsHidden = LayerBrush != null && !LayerBrush.SupportsTransformation;
|
||||||
@ -846,15 +865,15 @@ public sealed class Layer : RenderProfileElement
|
|||||||
}
|
}
|
||||||
|
|
||||||
OnLayerBrushUpdated();
|
OnLayerBrushUpdated();
|
||||||
ClearBrokenState("Failed to initialize layer brush");
|
ClearBrokenState(BROKEN_STATE_INIT_FAILED);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
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)
|
if (LayerBrush == null)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -54,7 +54,8 @@ public class ChangeLayerBrush : IProfileEditorCommand, IDisposable
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Undo()
|
public void Undo()
|
||||||
{
|
{
|
||||||
_layer.ChangeLayerBrush(_previousBrush);
|
if (_previousBrush != null)
|
||||||
|
_layer.ChangeLayerBrush(_previousBrush);
|
||||||
_executed = false;
|
_executed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
<!-- Custom styles -->
|
<!-- Custom styles -->
|
||||||
<StyleInclude Source="/Styles/Border.axaml" />
|
<StyleInclude Source="/Styles/Border.axaml" />
|
||||||
|
<StyleInclude Source="/Styles/BrokenState.axaml" />
|
||||||
<StyleInclude Source="/Styles/Skeleton.axaml" />
|
<StyleInclude Source="/Styles/Skeleton.axaml" />
|
||||||
<StyleInclude Source="/Styles/Button.axaml" />
|
<StyleInclude Source="/Styles/Button.axaml" />
|
||||||
<StyleInclude Source="/Styles/Condensed.axaml" />
|
<StyleInclude Source="/Styles/Condensed.axaml" />
|
||||||
|
|||||||
20
src/Artemis.UI.Shared/Styles/BrokenState.axaml
Normal file
20
src/Artemis.UI.Shared/Styles/BrokenState.axaml
Normal 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>
|
||||||
@ -4,21 +4,19 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
xmlns:profileTree="clr-namespace:Artemis.UI.Screens.ProfileEditor.ProfileTree"
|
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"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.Screens.ProfileEditor.ProfileTree.FolderTreeItemView"
|
x:Class="Artemis.UI.Screens.ProfileEditor.ProfileTree.FolderTreeItemView"
|
||||||
x:DataType="profileTree:FolderTreeItemViewModel">
|
x:DataType="profileTree:FolderTreeItemViewModel">
|
||||||
<Grid ColumnDefinitions="Auto,Auto,*,Auto,Auto">
|
<Grid ColumnDefinitions="Auto,Auto,*,Auto,Auto">
|
||||||
<Button Grid.Column="0"
|
<controls:HyperlinkButton Grid.Column="0"
|
||||||
ToolTip.Tip="{CompiledBinding ProfileElement.BrokenState}"
|
Classes="icon-button icon-button-small broken-state-button"
|
||||||
IsVisible="{CompiledBinding ProfileElement.BrokenState, Converter={x:Static ObjectConverters.IsNotNull}}"
|
Margin="0 0 5 0"
|
||||||
Classes="icon-button icon-button-small"
|
Command="{CompiledBinding ShowBrokenStateExceptions}"
|
||||||
Foreground="White"
|
IsVisible="{CompiledBinding ProfileElement.BrokenState, Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||||
Background="#E74C4C"
|
ToolTip.Tip="{CompiledBinding ProfileElement.BrokenState, FallbackValue=''}">
|
||||||
BorderBrush="#E74C4C"
|
|
||||||
Margin="0 0 5 0"
|
|
||||||
Command="{Binding ShowBrokenStateExceptions}">
|
|
||||||
<avalonia:MaterialIcon Kind="AlertCircle" />
|
<avalonia:MaterialIcon Kind="AlertCircle" />
|
||||||
</Button>
|
</controls:HyperlinkButton>
|
||||||
<avalonia:MaterialIcon Grid.Column="1"
|
<avalonia:MaterialIcon Grid.Column="1"
|
||||||
Kind="Folder"
|
Kind="Folder"
|
||||||
Margin="0 0 5 0"
|
Margin="0 0 5 0"
|
||||||
|
|||||||
@ -4,19 +4,19 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
xmlns:profileTree="clr-namespace:Artemis.UI.Screens.ProfileEditor.ProfileTree"
|
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"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.Screens.ProfileEditor.ProfileTree.LayerTreeItemView"
|
x:Class="Artemis.UI.Screens.ProfileEditor.ProfileTree.LayerTreeItemView"
|
||||||
x:DataType="profileTree:LayerTreeItemViewModel">
|
x:DataType="profileTree:LayerTreeItemViewModel">
|
||||||
<Grid ColumnDefinitions="Auto,Auto,*,Auto,Auto">
|
<Grid ColumnDefinitions="Auto,Auto,*,Auto,Auto">
|
||||||
<Button Grid.Column="0"
|
<controls:HyperlinkButton Grid.Column="0"
|
||||||
ToolTip.Tip="{CompiledBinding ProfileElement.BrokenState}"
|
Classes="icon-button icon-button-small broken-state-button"
|
||||||
IsVisible="{CompiledBinding ProfileElement.BrokenState, Converter={x:Static ObjectConverters.IsNotNull}}"
|
Margin="0 0 5 0"
|
||||||
Classes="icon-button icon-button-small"
|
Command="{CompiledBinding ShowBrokenStateExceptions}"
|
||||||
Foreground="#E74C4C"
|
IsVisible="{CompiledBinding ProfileElement.BrokenState, Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||||
Margin="0 0 5 0"
|
ToolTip.Tip="{CompiledBinding ProfileElement.BrokenState, FallbackValue=''}">
|
||||||
Command="{Binding ShowBrokenStateExceptions}">
|
|
||||||
<avalonia:MaterialIcon Kind="AlertCircle" />
|
<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" />
|
<avalonia:MaterialIcon Grid.Column="1" Kind="{CompiledBinding Layer.LayerBrush.Descriptor.Icon, FallbackValue=Layers}" Margin="0 0 5 0" />
|
||||||
<TextBox Grid.Column="2"
|
<TextBox Grid.Column="2"
|
||||||
Margin="-5 0 0 0"
|
Margin="-5 0 0 0"
|
||||||
|
|||||||
@ -39,21 +39,16 @@
|
|||||||
Background="{DynamicResource ContentDialogBackground}">
|
Background="{DynamicResource ContentDialogBackground}">
|
||||||
<Border Background="{DynamicResource TaskDialogHeaderBackground}">
|
<Border Background="{DynamicResource TaskDialogHeaderBackground}">
|
||||||
<Grid Classes="node-header" VerticalAlignment="Top" ColumnDefinitions="Auto,*,Auto,Auto">
|
<Grid Classes="node-header" VerticalAlignment="Top" ColumnDefinitions="Auto,*,Auto,Auto">
|
||||||
<Button Grid.Column="0"
|
<controls:HyperlinkButton Grid.Column="0"
|
||||||
VerticalAlignment="Center"
|
Classes="icon-button icon-button-small broken-state-button"
|
||||||
IsVisible="{CompiledBinding Node.BrokenState, Converter={x:Static ObjectConverters.IsNotNull}}"
|
Margin="5 0 0 0"
|
||||||
Classes="icon-button icon-button-small"
|
Command="{CompiledBinding ShowBrokenState}"
|
||||||
Foreground="White"
|
IsVisible="{CompiledBinding Node.BrokenState, Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||||
Background="#E74C4C"
|
ToolTip.Tip="{CompiledBinding Node.BrokenState}">
|
||||||
BorderBrush="#E74C4C"
|
<avalonia:MaterialIcon Kind="AlertCircle" />
|
||||||
Margin="5 0 0 0"
|
</controls:HyperlinkButton>
|
||||||
ToolTip.Tip="{CompiledBinding Node.BrokenState}"
|
|
||||||
Command="{CompiledBinding ShowBrokenState}">
|
|
||||||
<avalonia:MaterialIcon Kind="AlertCircle"></avalonia:MaterialIcon>
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<TextBlock Grid.Column="1" VerticalAlignment="Center" Margin="10 0 0 0" Text="{CompiledBinding Node.Name}" ToolTip.Tip="{CompiledBinding Node.Description}" />
|
<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"
|
<controls:HyperlinkButton Grid.Column="2"
|
||||||
IsVisible="{CompiledBinding Node.HelpUrl, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
|
IsVisible="{CompiledBinding Node.HelpUrl, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user