Browse Source

Basic Brush Picker works!

Krzysztof Krysiński 1 month ago
parent
commit
f4d116258c

+ 35 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Brushes/BrushOutputNode.cs

@@ -11,6 +11,7 @@ using PixiEditor.ChangeableDocument.Changeables.Brushes;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Context;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
+using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.ChangeableDocument.Rendering.ContextData;
@@ -24,6 +25,7 @@ public class BrushOutputNode : Node
     public const string NodeId = "BrushOutput";
     public const string BrushNameProperty = "BrushName";
     public const string FitToStrokeSizeProperty = "FitToStrokeSize";
+    public const int AutoGeneratedPreviewSize = 128;
 
     public InputProperty<string> BrushName { get; }
     public InputProperty<ShapeVectorData> VectorShape { get; }
@@ -43,6 +45,8 @@ public class BrushOutputNode : Node
 
     public InputProperty<IReadOnlyNodeGraph> Previous { get; }
 
+    public Texture AutoGeneratedPreview { get; private set; }
+
     internal Texture ContentTexture;
 
     private TextureCache cache = new();
@@ -181,6 +185,37 @@ public class BrushOutputNode : Node
         previewChunkyImage.CommitChanges();
         previewChunkyImage.DrawCommittedChunkOn(
             VecI.Zero, ChunkResolution.Full, surface.Canvas, VecD.Zero);
+
+        if (AutoGeneratedPreview == null || AutoGeneratedPreview.Size.X != AutoGeneratedPreviewSize ||
+            AutoGeneratedPreview.Size.Y != AutoGeneratedPreviewSize)
+        {
+            AutoGeneratedPreview?.Dispose();
+            AutoGeneratedPreview = Texture.ForDisplay(new VecI(AutoGeneratedPreviewSize));
+        }
+
+        AutoGeneratedPreview.DrawingSurface.Canvas.Clear();
+        AutoGeneratedPreview.DrawingSurface.Canvas.Save();
+        AutoGeneratedPreview.DrawingSurface.Canvas.Scale(
+            AutoGeneratedPreviewSize / (float)previewChunkyImage.CommittedSize.X,
+            AutoGeneratedPreviewSize / (float)previewChunkyImage.CommittedSize.Y);
+        previewChunkyImage.DrawCommittedChunkOn(
+            VecI.Zero, ChunkResolution.Full, AutoGeneratedPreview.DrawingSurface.Canvas, VecD.Zero, null, SamplingOptions.Bilinear);
+        AutoGeneratedPreview.DrawingSurface.Canvas.Restore();
+    }
+
+    public override void SerializeAdditionalData(Dictionary<string, object> additionalData)
+    {
+        base.SerializeAdditionalData(additionalData);
+        additionalData["PreviewTexture"] = AutoGeneratedPreview;
+    }
+
+    internal override void DeserializeAdditionalData(IReadOnlyDocument target, IReadOnlyDictionary<string, object> data, List<IChangeInfo> infos)
+    {
+        base.DeserializeAdditionalData(target, data, infos);
+        if (data.TryGetValue("PreviewTexture", out var texObj) && texObj is Texture tex)
+        {
+            AutoGeneratedPreview = tex;
+        }
     }
 
 

+ 2 - 0
src/PixiEditor.UI.Common/Accents/Base.axaml

@@ -13,6 +13,7 @@
             <Color x:Key="AccentColor">#B00022</Color>
             <Color x:Key="AccentHighColor">#c0334e</Color>
             <Color x:Key="ThemeAccent2Color">#5fad65</Color>
+            <Color x:Key="ThemeAccent2TranslucentColor">#7F5fad65</Color>
             <Color x:Key="ThemeAccent2HighColor">#3dd276</Color>
             <Color x:Key="ThemeAccent3Color">DodgerBlue</Color>
 
@@ -117,6 +118,7 @@
             <SolidColorBrush x:Key="ThemeAccentBrush" Color="{StaticResource AccentColor}" />
             <SolidColorBrush x:Key="ThemeAccentHighBrush" Color="{StaticResource AccentHighColor}" />
             <SolidColorBrush x:Key="ThemeAccent2Brush" Color="{StaticResource ThemeAccent2Color}" />
+            <SolidColorBrush x:Key="ThemeAccent2TranslucentBrush" Color="{StaticResource ThemeAccent2TranslucentColor}" />
             <SolidColorBrush x:Key="ThemeAccent3Brush" Color="{StaticResource ThemeAccent3Color}" />
 
             <SolidColorBrush x:Key="ThemeForegroundBrush" Color="{StaticResource ThemeForegroundColor}" />

BIN
src/PixiEditor/Data/Brushes/Basic.pixi


BIN
src/PixiEditor/Data/Brushes/Claws.pixi


BIN
src/PixiEditor/Data/Brushes/PixelCircle.pixi


BIN
src/PixiEditor/Data/Brushes/PixelSquare.pixi


+ 18 - 0
src/PixiEditor/Models/BrushEngine/Brush.cs

@@ -1,4 +1,5 @@
 using Avalonia.Platform;
+using Drawie.Backend.Core;
 using PixiEditor.ChangeableDocument.Changeables.Brushes;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Brushes;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
@@ -15,6 +16,7 @@ internal class Brush : IBrush
     public string Name { get; set; }
     public string? FilePath { get; }
     public Guid Id { get; }
+    public Texture Preview { get; }
 
     public Brush(Uri uri)
     {
@@ -49,6 +51,7 @@ internal class Brush : IBrush
 
         Name = name;
         Document = doc;
+        Preview = ExtractPreview(doc);
 
         stream.Close();
         stream.Dispose();
@@ -60,6 +63,7 @@ internal class Brush : IBrush
         Document = brushDocument;
         FilePath = brushDocument.FullFilePath;
         Id = brushDocument.NodeGraphHandler.AllNodes.OfType<BrushOutputNodeViewModel>().FirstOrDefault()?.Id ?? Guid.NewGuid();
+        Preview = ExtractPreview(brushDocument);
     }
 
     public Brush(string name, IDocument brushDocument, Guid id)
@@ -68,6 +72,20 @@ internal class Brush : IBrush
         Document = brushDocument;
         FilePath = brushDocument.FullFilePath;
         Id = id;
+        Preview = ExtractPreview(brushDocument);
+    }
+
+    private Texture? ExtractPreview(IDocument brushDocument)
+    {
+        using var graph = brushDocument.ShareGraph();
+        BrushOutputNode outputNode =
+            graph.TryAccessData().AllNodes.OfType<BrushOutputNode>().FirstOrDefault();
+        if (outputNode != null && outputNode.AutoGeneratedPreview != null)
+        {
+            return outputNode.AutoGeneratedPreview;
+        }
+
+        return null;
     }
 
     public override string ToString()

+ 7 - 1
src/PixiEditor/Models/Serialization/Factories/TextureSerializationFactory.cs

@@ -5,8 +5,12 @@ namespace PixiEditor.Models.Serialization.Factories;
 public class TextureSerializationFactory : SerializationFactory<byte[], Texture>
 {
     private SurfaceSerializationFactory SurfaceFactory { get; } = new SurfaceSerializationFactory();
+
     public override byte[] Serialize(Texture original)
     {
+        SurfaceFactory.Config = Config;
+        SurfaceFactory.ResourceLocator = ResourceLocator;
+
         Surface surface = new Surface(original.Size);
         surface.DrawingSurface.Canvas.DrawSurface(original.DrawingSurface, 0, 0);
         return SurfaceFactory.Serialize(surface);
@@ -15,6 +19,8 @@ public class TextureSerializationFactory : SerializationFactory<byte[], Texture>
     public override bool TryDeserialize(object serialized, out Texture original,
         (string serializerName, string serializerVersion) serializerData)
     {
+        SurfaceFactory.Config = Config;
+        SurfaceFactory.ResourceLocator = ResourceLocator;
         if (serialized is byte[] imgBytes)
         {
             if (SurfaceFactory.TryDeserialize(imgBytes, out Surface surface, serializerData))
@@ -26,7 +32,7 @@ public class TextureSerializationFactory : SerializationFactory<byte[], Texture>
         }
 
         original = null;
-        return false; 
+        return false;
     }
 
 

+ 55 - 0
src/PixiEditor/Views/Input/BrushItem.axaml

@@ -0,0 +1,55 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+             xmlns:visuals="clr-namespace:PixiEditor.Views.Visuals"
+             xmlns:localization="clr-namespace:PixiEditor.UI.Common.Localization;assembly=PixiEditor.UI.Common"
+             xmlns:input="clr-namespace:PixiEditor.Views.Input"
+             xmlns:surfaces="clr-namespace:Drawie.Backend.Core.Surfaces;assembly=Drawie.Backend.Core"
+             xmlns:controls="clr-namespace:Drawie.Interop.Avalonia.Core.Controls;assembly=Drawie.Interop.Avalonia.Core"
+             mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+             x:Class="PixiEditor.Views.Input.BrushItem">
+    <UserControl.Styles>
+        <Style Selector="Button#FavouriteButton">
+            <Setter Property="Content" Value="{DynamicResource icon-star}" />
+        </Style>
+        <Style Selector="input|BrushItem:favourite Button#FavouriteButton">
+            <Setter Property="Content" Value="{DynamicResource icon-star-filled}" />
+        </Style>
+    </UserControl.Styles>
+    <Grid>
+        <DockPanel LastChildFill="True"
+                   DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}}">
+            <controls:DrawieTextureControl Height="32" Width="32"
+                                    SamplingOptions="Bilinear"
+                                    Margin="0, 0, 5, 0"
+                                    Texture="{Binding Brush.Preview}" DockPanel.Dock="Left" />
+            <TextBlock VerticalAlignment="Center"
+                       Text="{Binding Brush.Name}" />
+
+            <StackPanel Orientation="Horizontal" DockPanel.Dock="Right">
+                <Button
+                    Name="FavouriteButton" Width="28" Height="28"
+                    Classes="pixi-icon" Padding="2"
+                    localization:Translator.TooltipKey="ADD_TO_FAVORITES">
+                    <Button.Styles>
+                        <Style Selector="Button:pointerover">
+                            <Setter Property="Background" Value="Orange" />
+                        </Style>
+                    </Button.Styles>
+                </Button>
+                <Button Name="deleteButton"
+                        Classes="pixi-icon" Width="28" Height="28"
+                        Content="{DynamicResource icon-trash}"
+                        localization:Translator.TooltipKey="DELETE" Padding="2" Margin="0" Cursor="Hand">
+                    <Button.Styles>
+                        <Style Selector="Button:pointerover">
+                            <Setter Property="Background" Value="Red" />
+                        </Style>
+                    </Button.Styles>
+                </Button>
+            </StackPanel>
+            <Panel />
+        </DockPanel>
+    </Grid>
+</UserControl>

+ 24 - 0
src/PixiEditor/Views/Input/BrushItem.axaml.cs

@@ -0,0 +1,24 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Metadata;
+using Avalonia.Markup.Xaml;
+using PixiEditor.Models.BrushEngine;
+
+namespace PixiEditor.Views.Input;
+
+internal partial class BrushItem : UserControl
+{
+    public static readonly StyledProperty<Brush> BrushProperty = AvaloniaProperty.Register<BrushItem, Brush>("Brush");
+
+    public BrushItem()
+    {
+        InitializeComponent();
+    }
+
+    public Brush Brush
+    {
+        get { return (Brush)GetValue(BrushProperty); }
+        set { SetValue(BrushProperty, value); }
+    }
+}
+

+ 118 - 56
src/PixiEditor/Views/Input/BrushPicker.axaml

@@ -4,70 +4,132 @@
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
              xmlns:input="clr-namespace:PixiEditor.Views.Input"
              xmlns:localization="clr-namespace:PixiEditor.UI.Common.Localization;assembly=PixiEditor.UI.Common"
+             xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
+             xmlns:controls="clr-namespace:Drawie.Interop.Avalonia.Core.Controls;assembly=Drawie.Interop.Avalonia.Core"
              mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
              x:ClassModifier="internal"
              x:Class="PixiEditor.Views.Input.BrushPicker">
-    <ToggleButton>
-        <ToggleButton.Styles>
-            <Style Selector="FlyoutPresenter">
-                <Setter Property="Cursor" Value="Arrow"/>
-            </Style>
-        </ToggleButton.Styles>
-        <ToggleButton.Flyout>
-            <Flyout
-                IsOpen="{Binding IsChecked, RelativeSource={RelativeSource AncestorType=ToggleButton}, Mode=TwoWay}"
-                Placement="BottomEdgeAlignedLeft"
-                ShowMode="Transient">
-                <Grid Width="300" Height="400" Margin="10">
-                    <Grid.RowDefinitions>
-                        <RowDefinition Height="Auto" />
-                        <RowDefinition Height="*" />
-                    </Grid.RowDefinitions>
-
-                    <Grid>
-                        <Grid.ColumnDefinitions>
-                            <ColumnDefinition Width="*" />
-                            <ColumnDefinition Width="*" />
-                            <ColumnDefinition Width="Auto" />
-                        </Grid.ColumnDefinitions>
+    <Panel>
+        <controls:DrawieTextureControl
+            Width="40" Height="20"
+            ZIndex="1"
+            SamplingOptions="Bilinear"
+            Texture="{Binding $parent[input:BrushPicker].SelectedBrush.Preview, FallbackValue={x:Null}}" />
+        <ToggleButton Name="PopupToggle" Width="40" Height="20">
+            <ToggleButton.Styles>
+                <Style Selector="FlyoutPresenter">
+                    <Setter Property="Cursor" Value="Arrow" />
+                </Style>
+            </ToggleButton.Styles>
+            <ToggleButton.Flyout>
+                <Flyout
+                    IsOpen="{Binding IsChecked, RelativeSource={RelativeSource AncestorType=ToggleButton}, Mode=TwoWay}"
+                    Placement="BottomEdgeAlignedLeft"
+                    ShowMode="Transient">
+                    <Grid Width="300" Height="400" Margin="10">
                         <Grid.RowDefinitions>
                             <RowDefinition Height="Auto" />
-                            <RowDefinition Height="Auto" />
+                            <RowDefinition Height="*" />
                         </Grid.RowDefinitions>
 
-                        <TextBox Watermark="{localization:Translate Key=SEARCH}" />
-                        <ComboBox Grid.Column="1" Margin="5,0,0,0" />
-                        <StackPanel Orientation="Horizontal" Grid.Column="2" Grid.Row="0" Spacing="5"
-                                    Margin="5,0,0,0">
-                            <ToggleButton Classes="pixi-icon"
-                                          Name="SortButton"
-                                          Content="{DynamicResource icon-arrow-up}" />
-                            <ToggleButton Classes="pixi-icon"
-                                          Content="{DynamicResource icon-grid}" />
-                        </StackPanel>
+                        <Grid>
+                            <Grid.ColumnDefinitions>
+                                <ColumnDefinition Width="*" />
+                                <ColumnDefinition Width="*" />
+                                <ColumnDefinition Width="Auto" />
+                            </Grid.ColumnDefinitions>
+                            <Grid.RowDefinitions>
+                                <RowDefinition Height="Auto" />
+                                <RowDefinition Height="Auto" />
+                            </Grid.RowDefinitions>
+
+                            <TextBox Watermark="{localization:Translate Key=SEARCH}">
+                                <TextBox.InnerLeftContent>
+                                    <TextBlock Classes="pixi-icon" Text="{DynamicResource icon-search}"
+                                               Margin="0, 0, 5, 0" />
+                                </TextBox.InnerLeftContent>
+                            </TextBox>
+                            <ComboBox Grid.Column="1" Margin="5,0,0,0" />
+                            <StackPanel Orientation="Horizontal" Grid.Column="2" Grid.Row="0" Spacing="5"
+                                        Margin="5,0,0,0">
+                                <ToggleButton Classes="pixi-icon"
+                                              Name="SortButton"
+                                              Content="{DynamicResource icon-arrow-up}" />
+                                <ToggleButton Classes="pixi-icon"
+                                              Content="{DynamicResource icon-grid}" />
+                            </StackPanel>
+
+                            <StackPanel Orientation="Horizontal" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
+                                        HorizontalAlignment="Right" Margin="0,5,5,0" Spacing="5"
+                                        IsVisible="{Binding IsChecked, ElementName=SortButton}">
+                                <TextBlock Text="{localization:Translate Key=SORT_BY}" />
+                                <ComboBox SelectedIndex="0">
+                                    <ComboBoxItem Content="{localization:Translate Key=ALPHABETICAL}" />
+                                </ComboBox>
+                            </StackPanel>
+
+                            <StackPanel Margin="5, 5, 0, 0" Orientation="Horizontal" Grid.Column="2" Grid.Row="1"
+                                        Spacing="5">
+                                <ToggleButton
+                                    IsVisible="{Binding IsChecked, ElementName=SortButton}"
+                                    Content="{DynamicResource icon-arrow-up}"
+                                    Classes="pixi-icon" />
+                                <ToggleButton
+                                    IsVisible="{Binding IsChecked, ElementName=SortButton}"
+                                    Content="{DynamicResource icon-arrow-down}"
+                                    Classes="pixi-icon" />
+                            </StackPanel>
+                        </Grid>
 
-                        <StackPanel Orientation="Horizontal" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
-                                    HorizontalAlignment="Right" Margin="0,5,5,0" Spacing="5"
-                                    IsVisible="{Binding IsChecked, ElementName=SortButton}">
-                            <TextBlock Text="{localization:Translate Key=SORT_BY}" />
-                            <ComboBox SelectedIndex="0">
-                                <ComboBoxItem Content="{localization:Translate Key=ALPHABETICAL}" />
-                            </ComboBox>
-                        </StackPanel>
+                        <ScrollViewer Grid.Row="1" Margin="0,10,0,0" HorizontalScrollBarVisibility="Disabled"
+                                      VerticalScrollBarVisibility="Auto">
+                            <ItemsControl
+                                ItemsSource="{Binding $parent[input:BrushPicker].Brushes}">
+                                <ItemsControl.Styles>
+                                    <Style Selector="ContentPresenter > Border">
+                                        <Setter Property="Background" Value="Transparent" />
+                                        <Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}" />
+                                        <Setter Property="BorderThickness" Value="0, 0, 0, 1" />
+                                        <Setter Property="Padding" Value="6, 6, 6, 5" />
+                                    </Style>
+                                    <Style Selector="ContentPresenter:pointerover > Border">
+                                        <Setter Property="Background" Value="{DynamicResource ThemeControlHighBrush}" />
+                                        <Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderHighBrush}" />
+                                        <Setter Property="BorderThickness" Value="1" />
+                                        <Setter Property="Padding" Value="5" />
+                                    </Style>
 
-                        <StackPanel Margin="5, 5, 0, 0" Orientation="Horizontal" Grid.Column="2" Grid.Row="1" Spacing="5">
-                            <ToggleButton
-                                          IsVisible="{Binding IsChecked, ElementName=SortButton}"
-                                          Content="{DynamicResource icon-arrow-up}"
-                                          Classes="pixi-icon" />
-                            <ToggleButton
-                                          IsVisible="{Binding IsChecked, ElementName=SortButton}"
-                                          Content="{DynamicResource icon-arrow-down}"
-                                          Classes="pixi-icon" />
-                        </StackPanel>
+                                    <Style Selector="ContentPresenter > Border.selected">
+                                        <Setter Property="Background"
+                                                Value="{DynamicResource ThemeAccent2TranslucentBrush}" />
+                                        <Setter Property="BorderBrush" Value="{DynamicResource ThemeAccent2Brush}" />
+                                        <Setter Property="BorderThickness" Value="1" />
+                                        <Setter Property="Padding" Value="5" />
+                                    </Style>
+                                </ItemsControl.Styles>
+                                <ItemsControl.ItemTemplate>
+                                    <DataTemplate>
+                                        <Border CornerRadius="4" IsHitTestVisible="True">
+                                            <input:BrushItem Brush="{Binding .}" />
+                                            <Interaction.Behaviors>
+                                                <ExecuteCommandOnPointerPressedBehavior
+                                                    Command="{Binding $parent[input:BrushPicker].SelectBrushCommand}"
+                                                    CommandParameter="{Binding }" />
+                                            </Interaction.Behaviors>
+                                            <Classes.selected>
+                                                <MultiBinding Converter="{converters:AreEqualConverter}">
+                                                    <Binding Path="$parent[input:BrushPicker].SelectedBrush" />
+                                                    <Binding />
+                                                </MultiBinding>
+                                            </Classes.selected>
+                                        </Border>
+                                    </DataTemplate>
+                                </ItemsControl.ItemTemplate>
+                            </ItemsControl>
+                        </ScrollViewer>
                     </Grid>
-                </Grid>
-            </Flyout>
-        </ToggleButton.Flyout>
-    </ToggleButton>
+                </Flyout>
+            </ToggleButton.Flyout>
+        </ToggleButton>
+    </Panel>
 </UserControl>

+ 9 - 59
src/PixiEditor/Views/Input/BrushPicker.axaml.cs

@@ -4,21 +4,13 @@ using Avalonia.Controls;
 using Avalonia.Markup.Xaml;
 using Avalonia.Media;
 using Avalonia.VisualTree;
+using CommunityToolkit.Mvvm.Input;
 using Brush = PixiEditor.Models.BrushEngine.Brush;
 
 namespace PixiEditor.Views.Input;
 
 internal partial class BrushPicker : UserControl
 {
-    public static readonly StyledProperty<int> BrushIndexProperty = AvaloniaProperty.Register<BrushPicker, int>(
-        nameof(BrushIndex));
-
-    public int BrushIndex
-    {
-        get => GetValue(BrushIndexProperty);
-        set => SetValue(BrushIndexProperty, value);
-    }
-
     public static readonly StyledProperty<ObservableCollection<Brush>> BrushesProperty =
         AvaloniaProperty.Register<BrushPicker, ObservableCollection<Brush>>(
             nameof(Brushes));
@@ -39,23 +31,13 @@ internal partial class BrushPicker : UserControl
         set => SetValue(SelectedBrushProperty, value);
     }
 
-    private bool suppressUpdate = false;
-
     static BrushPicker()
     {
-        BrushesProperty.Changed.AddClassHandler<BrushPicker>((x, e) => x.OnBrushesChanged(e));
-        BrushIndexProperty.Changed.AddClassHandler<BrushPicker>((x, e) => x.OnBrushIndexChanged(e));
-        SelectedBrushProperty.Changed.AddClassHandler<BrushPicker>((x, e) =>
+        BrushesProperty.Changed.AddClassHandler<BrushPicker>((x, e) =>
         {
-            if (e.NewValue is Brush newBrush && x.Brushes != null)
+            if (x.SelectedBrush == null && x.Brushes.Count > 0)
             {
-                int index = x.Brushes.IndexOf(newBrush);
-                if (index != -1 && x.BrushIndex != index)
-                {
-                    x.suppressUpdate = true;
-                    x.BrushIndex = index;
-                    x.suppressUpdate = false;
-                }
+                x.SelectedBrush = x.Brushes[0];
             }
         });
     }
@@ -65,43 +47,11 @@ internal partial class BrushPicker : UserControl
         InitializeComponent();
     }
 
-    private void OnBrushesChanged(AvaloniaPropertyChangedEventArgs e)
-    {
-        if (e.NewValue is ObservableCollection<Brush> brushes && brushes.Count > 0)
-        {
-            BrushIndex = brushes.IndexOf(SelectedBrush);
-            if (BrushIndex == -1)
-            {
-                BrushIndex = 0;
-                SelectedBrush = brushes[0];
-            }
-        }
-    }
-
-    protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
-    {
-        if(SelectedBrush != null && BrushIndex == -1 && Brushes != null)
-        {
-            int index = Brushes.IndexOf(SelectedBrush);
-            if (index != -1)
-            {
-                BrushIndex = index;
-            }
-        }
-    }
-
-    private void OnBrushIndexChanged(AvaloniaPropertyChangedEventArgs e)
+    [RelayCommand]
+    public void SelectBrush(Brush brush)
     {
-        if (suppressUpdate || (e.Sender is Visual v && !v.IsAttachedToVisualTree()))
-            return;
-
-        if (e.NewValue is int index && Brushes != null && index >= 0 && index < Brushes.Count)
-        {
-            SelectedBrush = Brushes[index];
-        }
-        else
-        {
-            SelectedBrush = null;
-        }
+        SelectedBrush = brush;
+        PopupToggle.IsChecked = false;
+        PopupToggle.Flyout.Hide();
     }
 }