Browse Source

Added background customization settings

Krzysztof Krysiński 3 months ago
parent
commit
d9ac51f52f

+ 7 - 0
src/PixiEditor.Extensions.CommonApi/UserPreferences/PreferencesConstants.cs

@@ -43,4 +43,11 @@ public static class PreferencesConstants
     public const string CustomBackgroundScaleX = "CustomBackgroundScaleX";
     public const string CustomBackgroundScaleY = "CustomBackgroundScaleY";
     public const double CustomBackgroundScaleDefault = 16;
+
+    public const string PrimaryBackgroundColorDefault = "#616161";
+    public const string PrimaryBackgroundColor = "PrimaryBackgroundColor";
+
+    public const string SecondaryBackgroundColorDefault = "#353535";
+    public const string SecondaryBackgroundColor = "SecondaryBackgroundColor";
+
 }

+ 4 - 0
src/PixiEditor.Extensions.CommonApi/UserPreferences/Settings/PixiEditor/PixiEditorSettings.cs

@@ -77,5 +77,9 @@ public static class PixiEditorSettings
         public static SyncedSetting<bool> AutoScaleBackground { get; } = SyncedSetting.NonOwned(PixiEditor, PreferencesConstants.AutoScaleBackgroundDefault, PreferencesConstants.AutoScaleBackground);
         public static SyncedSetting<double> CustomBackgroundScaleX { get; } = SyncedSetting.NonOwned(PixiEditor, PreferencesConstants.CustomBackgroundScaleDefault, PreferencesConstants.CustomBackgroundScaleX);
         public static SyncedSetting<double> CustomBackgroundScaleY { get; } = SyncedSetting.NonOwned(PixiEditor, PreferencesConstants.CustomBackgroundScaleDefault, PreferencesConstants.CustomBackgroundScaleY);
+
+        public static SyncedSetting<string> PrimaryBackgroundColor { get; } = SyncedSetting.NonOwned(PixiEditor, PreferencesConstants.PrimaryBackgroundColorDefault, PreferencesConstants.PrimaryBackgroundColor);
+        public static SyncedSetting<string> SecondaryBackgroundColor { get; } = SyncedSetting.NonOwned(PixiEditor, PreferencesConstants.SecondaryBackgroundColorDefault, PreferencesConstants.SecondaryBackgroundColor);
+
     }
 }

+ 3 - 0
src/PixiEditor/Data/Localization/Languages/en.json

@@ -1030,4 +1030,7 @@
   "UPDATES": "Updates",
   "SCENE": "Scene",
   "CUSTOM_BACKGROUND_SCALE": "Custom background scale",
+  "PRIMARY_BG_COLOR": "Primary background color",
+  "SECONDARY_BG_COLOR": "Secondary background color",
+  "RESET": "Reset"
 }

+ 49 - 0
src/PixiEditor/ViewModels/SubViewModels/ViewportWindowViewModel.cs

@@ -1,5 +1,9 @@
 using System.ComponentModel;
 using Avalonia.Threading;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
 using PixiDocks.Core.Docking;
 using PixiDocks.Core.Docking.Events;
 using PixiEditor.Helpers.UI;
@@ -126,6 +130,17 @@ internal class ViewportWindowViewModel : SubViewModel<WindowViewModel>, IDockabl
         }
     }
 
+    private Bitmap backgroundBitmap;
+    public Bitmap BackgroundBitmap
+    {
+        get => backgroundBitmap;
+        set
+        {
+            backgroundBitmap = value;
+            OnPropertyChanged(nameof(BackgroundBitmap));
+        }
+    }
+
     private PreviewPainterControl previewPainterControl;
 
     public void IndexChanged()
@@ -145,10 +160,15 @@ internal class ViewportWindowViewModel : SubViewModel<WindowViewModel>, IDockabl
         AutoScaleBackground = PixiEditorSettings.Scene.AutoScaleBackground.Value;
         CustomBackgroundScaleX = PixiEditorSettings.Scene.CustomBackgroundScaleX.Value;
         CustomBackgroundScaleY = PixiEditorSettings.Scene.CustomBackgroundScaleY.Value;
+        BackgroundBitmap = BitmapFromColors(
+            PixiEditorSettings.Scene.PrimaryBackgroundColor.Value,
+            PixiEditorSettings.Scene.SecondaryBackgroundColor.Value);
 
         PixiEditorSettings.Scene.AutoScaleBackground.ValueChanged += UpdateAutoScaleBackground;
         PixiEditorSettings.Scene.CustomBackgroundScaleX.ValueChanged += UpdateCustomBackgroundScaleX;
         PixiEditorSettings.Scene.CustomBackgroundScaleY.ValueChanged += UpdateCustomBackgroundScaleY;
+        PixiEditorSettings.Scene.PrimaryBackgroundColor.ValueChanged += UpdateBackgroundBitmap;
+        PixiEditorSettings.Scene.SecondaryBackgroundColor.ValueChanged += UpdateBackgroundBitmap;
 
         previewPainterControl = new PreviewPainterControl(Document.PreviewPainter,
             Document.AnimationDataViewModel.ActiveFrameTime.Frame);
@@ -200,6 +220,8 @@ internal class ViewportWindowViewModel : SubViewModel<WindowViewModel>, IDockabl
                         PixiEditorSettings.Scene.AutoScaleBackground.ValueChanged -= UpdateAutoScaleBackground;
                         PixiEditorSettings.Scene.CustomBackgroundScaleX.ValueChanged -= UpdateCustomBackgroundScaleX;
                         PixiEditorSettings.Scene.CustomBackgroundScaleY.ValueChanged -= UpdateCustomBackgroundScaleY;
+                        PixiEditorSettings.Scene.PrimaryBackgroundColor.ValueChanged -= UpdateBackgroundBitmap;
+                        PixiEditorSettings.Scene.SecondaryBackgroundColor.ValueChanged -= UpdateBackgroundBitmap;
                     }
                 });
             });
@@ -223,6 +245,33 @@ internal class ViewportWindowViewModel : SubViewModel<WindowViewModel>, IDockabl
         CustomBackgroundScaleY = newValue;
     }
 
+    private void UpdateBackgroundBitmap(Setting<string> setting, string newValue)
+    {
+        BackgroundBitmap?.Dispose();
+        BackgroundBitmap = BitmapFromColors(
+            PixiEditorSettings.Scene.PrimaryBackgroundColor.Value,
+            PixiEditorSettings.Scene.SecondaryBackgroundColor.Value);
+    }
+
+    private static Bitmap BitmapFromColors(string primaryHex, string secondaryHex)
+    {
+        Color primary = Color.FromHex(primaryHex);
+        Color secondary = Color.FromHex(secondaryHex);
+
+        Surface surface = Surface.ForDisplay(new VecI(2, 2));
+        surface.DrawingSurface.Canvas.Clear(primary);
+        using Paint secondaryPaint = new Paint
+        {
+            Color = secondary,
+            Style = PaintStyle.Fill
+        };
+        surface.DrawingSurface.Canvas.DrawRect(1, 0, 1, 1, secondaryPaint);
+        surface.DrawingSurface.Canvas.DrawRect(0, 1, 1, 1, secondaryPaint);
+
+        using var snapshot = surface.DrawingSurface.Snapshot();
+        return Bitmap.FromImage(snapshot);
+    }
+
     private static SavedState GetSaveState(DocumentViewModel document)
     {
         if (document.AllChangesSaved)

+ 42 - 1
src/PixiEditor/ViewModels/UserPreferences/Settings/SceneSettings.cs

@@ -1,5 +1,9 @@
-using Drawie.Numerics;
+using System.Windows.Input;
+using Avalonia.Media;
+using CommunityToolkit.Mvvm.Input;
+using Drawie.Numerics;
 using PixiEditor.Extensions.CommonApi.UserPreferences;
+using PixiEditor.Helpers.Extensions;
 
 namespace PixiEditor.ViewModels.UserPreferences.Settings;
 
@@ -25,4 +29,41 @@ internal class SceneSettings : SettingsGroup
         get => customBackgroundScaleY;
         set => RaiseAndUpdatePreference(ref customBackgroundScaleY, value);
     }
+
+    private string _primaryBackgroundColorHex = GetPreference(PreferencesConstants.PrimaryBackgroundColor, PreferencesConstants.PrimaryBackgroundColorDefault);
+    public string PrimaryBackgroundColorHex
+    {
+        get => _primaryBackgroundColorHex;
+        set => RaiseAndUpdatePreference(ref _primaryBackgroundColorHex, value, PreferencesConstants.PrimaryBackgroundColor);
+    }
+
+    private string _secondaryBackgroundColorHex = GetPreference(PreferencesConstants.SecondaryBackgroundColor, PreferencesConstants.SecondaryBackgroundColorDefault);
+    public string SecondaryBackgroundColorHex
+    {
+        get => _secondaryBackgroundColorHex;
+        set => RaiseAndUpdatePreference(ref _secondaryBackgroundColorHex, value, PreferencesConstants.SecondaryBackgroundColor);
+    }
+
+    public Color PrimaryBackgroundColor
+    {
+        get => Color.Parse(PrimaryBackgroundColorHex);
+        set => PrimaryBackgroundColorHex = value.ToColor().ToRgbHex();
+    }
+
+    public Color SecondaryBackgroundColor
+    {
+        get => Color.Parse(SecondaryBackgroundColorHex);
+        set => SecondaryBackgroundColorHex = value.ToColor().ToRgbHex();
+    }
+
+    public ICommand ResetBackgroundCommand { get; }
+
+    public SceneSettings()
+    {
+        ResetBackgroundCommand = new RelayCommand(() =>
+        {
+            PrimaryBackgroundColorHex = PreferencesConstants.PrimaryBackgroundColorDefault;
+            SecondaryBackgroundColorHex = PreferencesConstants.SecondaryBackgroundColorDefault;
+        });
+    }
 }

+ 1 - 0
src/PixiEditor/Views/Dock/DocumentTemplate.axaml

@@ -43,6 +43,7 @@
         AutoBackgroundScale="{Binding AutoScaleBackground, Mode=OneWay}"
         CustomBackgroundScaleX="{Binding CustomBackgroundScaleX, Mode=OneWay}"
         CustomBackgroundScaleY="{Binding CustomBackgroundScaleY, Mode=OneWay}"
+        BackgroundBitmap="{Binding BackgroundBitmap, Mode=OneWay}"
         HudVisible="{Binding HudVisible}"
         Document="{Binding Document}">
     </viewportControls:Viewport>

+ 1 - 1
src/PixiEditor/Views/Main/ViewportControls/Viewport.axaml

@@ -211,7 +211,7 @@
             AutoBackgroundScale="{Binding AutoBackgroundScale, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWay}"
             CustomBackgroundScaleX="{Binding CustomBackgroundScaleX, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWay}"
             CustomBackgroundScaleY="{Binding CustomBackgroundScaleY, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWay}"
-            CheckerImagePath="/Images/CheckerTile.png"
+            BackgroundBitmap="{Binding BackgroundBitmap, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWay}"
             PointerPressed="Scene_OnContextMenuOpening"
             ui:RenderOptionsBindable.BitmapInterpolationMode="{Binding Scale, Converter={converters:ScaleToBitmapScalingModeConverter}, RelativeSource={RelativeSource Self}}">
             <rendering:Scene.ContextFlyout>

+ 10 - 0
src/PixiEditor/Views/Main/ViewportControls/Viewport.axaml.cs

@@ -13,6 +13,7 @@ using PixiEditor.ViewModels;
 using PixiEditor.Views.Visuals;
 using Drawie.Backend.Core;
 using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces;
 using PixiEditor.Helpers.Behaviours;
 using PixiEditor.Helpers.UI;
 using PixiEditor.Models.Controllers.InputDevice;
@@ -123,6 +124,15 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
     public static readonly StyledProperty<double> CustomBackgroundScaleYProperty = AvaloniaProperty.Register<Viewport, double>(
         nameof(CustomBackgroundScaleY));
 
+    public static readonly StyledProperty<Bitmap> BackgroundBitmapProperty = AvaloniaProperty.Register<Viewport, Bitmap>(
+        nameof(BackgroundBitmap));
+
+    public Bitmap BackgroundBitmap
+    {
+        get => GetValue(BackgroundBitmapProperty);
+        set => SetValue(BackgroundBitmapProperty, value);
+    }
+
     public double CustomBackgroundScaleY
     {
         get => GetValue(CustomBackgroundScaleYProperty);

+ 12 - 26
src/PixiEditor/Views/Rendering/Scene.cs

@@ -51,9 +51,6 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
         AvaloniaProperty.Register<Scene, ObservableCollection<Overlay>>(
             nameof(AllOverlays));
 
-    public static readonly StyledProperty<string> CheckerImagePathProperty = AvaloniaProperty.Register<Scene, string>(
-        nameof(CheckerImagePath));
-
     public static readonly StyledProperty<Cursor> DefaultCursorProperty = AvaloniaProperty.Register<Scene, Cursor>(
         nameof(DefaultCursor));
 
@@ -92,6 +89,15 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
         set => SetValue(CustomBackgroundScaleYProperty, value);
     }
 
+    public static readonly StyledProperty<Bitmap> BackgroundBitmapProperty = AvaloniaProperty.Register<Scene, Bitmap>(
+        nameof(BackgroundBitmap));
+
+    public Bitmap BackgroundBitmap
+    {
+        get => GetValue(BackgroundBitmapProperty);
+        set => SetValue(BackgroundBitmapProperty, value);
+    }
+
     public SceneRenderer SceneRenderer
     {
         get => GetValue(SceneRendererProperty);
@@ -104,12 +110,6 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
         set => SetValue(DefaultCursorProperty, value);
     }
 
-    public string CheckerImagePath
-    {
-        get => GetValue(CheckerImagePathProperty);
-        set => SetValue(CheckerImagePathProperty, value);
-    }
-
     public ObservableCollection<Overlay> AllOverlays
     {
         get => GetValue(AllOverlaysProperty);
@@ -140,8 +140,6 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
         set { SetValue(RenderOutputProperty, value); }
     }
 
-    private Bitmap? checkerBitmap;
-
     private Overlay? capturedOverlay;
 
     private List<Overlay> mouseOverOverlays = new();
@@ -180,7 +178,6 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
             AutoBackgroundScaleProperty, CustomBackgroundScaleXProperty, CustomBackgroundScaleYProperty);
 
         FadeOutProperty.Changed.AddClassHandler<Scene>(FadeOutChanged);
-        CheckerImagePathProperty.Changed.AddClassHandler<Scene>(CheckerImagePathChanged);
         AllOverlaysProperty.Changed.AddClassHandler<Scene>(ActiveOverlaysChanged);
         DefaultCursorProperty.Changed.AddClassHandler<Scene>(DefaultCursorChanged);
         ChannelsProperty.Changed.AddClassHandler<Scene>(Refresh);
@@ -190,6 +187,7 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
         AutoBackgroundScaleProperty.Changed.AddClassHandler<Scene>(Refresh);
         CustomBackgroundScaleXProperty.Changed.AddClassHandler<Scene>(Refresh);
         CustomBackgroundScaleYProperty.Changed.AddClassHandler<Scene>(Refresh);
+        BackgroundBitmapProperty.Changed.AddClassHandler<Scene>(Refresh);
     }
 
     private static void Refresh(Scene scene, AvaloniaPropertyChangedEventArgs args)
@@ -306,7 +304,7 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
 
     private void DrawCheckerboard(DrawingSurface surface, RectD dirtyBounds)
     {
-        if (checkerBitmap == null) return;
+        if (BackgroundBitmap == null) return;
 
         RectD operationSurfaceRectToRender = new RectD(0, 0, dirtyBounds.Width, dirtyBounds.Height);
         VecD checkerScale = AutoBackgroundScale
@@ -318,7 +316,7 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
         checkerPaint = new Paint
         {
             Shader = Shader.CreateBitmap(
-                checkerBitmap,
+                BackgroundBitmap,
                 TileMode.Repeat, TileMode.Repeat,
                 Matrix3X3.CreateScale((float)checkerScale.X, (float)checkerScale.Y)),
             FilterQuality = FilterQuality.None
@@ -796,18 +794,6 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
         }
     }
 
-    private static void CheckerImagePathChanged(Scene scene, AvaloniaPropertyChangedEventArgs e)
-    {
-        if (e.NewValue is string path)
-        {
-            scene.checkerBitmap = ImagePathToBitmapConverter.LoadDrawingApiBitmapFromRelativePath(path);
-        }
-        else
-        {
-            scene.checkerBitmap = null;
-        }
-    }
-
     private static void DocumentChanged(Scene scene, AvaloniaPropertyChangedEventArgs e)
     {
         if (e.OldValue is DocumentViewModel oldDocumentViewModel)

+ 18 - 0
src/PixiEditor/Views/Windows/Settings/SettingsWindow.axaml

@@ -17,6 +17,7 @@
     xmlns:dialogs="clr-namespace:PixiEditor.Views.Dialogs"
     xmlns:settings="clr-namespace:PixiEditor.Views.Windows.Settings"
     xmlns:localization="clr-namespace:PixiEditor.Extensions.Common.Localization;assembly=PixiEditor.Extensions"
+    xmlns:colorPicker="clr-namespace:ColorPicker;assembly=ColorPicker.AvaloniaUI"
     mc:Ignorable="d"
     x:Class="PixiEditor.Views.Windows.Settings.SettingsWindow"
     Name="window"
@@ -379,6 +380,23 @@
                                                  Size="{Binding SettingsSubViewModel.Scene.CustomBackgroundScaleY, Mode=TwoWay}"
                                                  HorizontalAlignment="Left" />
                             </StackPanel>
+
+                            <StackPanel Orientation="Horizontal" Spacing="5">
+                                <TextBlock ui:Translator.Key="PRIMARY_BG_COLOR" />
+                                <colorPicker:PortableColorPicker Width="40" Height="20" EnableGradientsTab="False"
+                                                                 SelectedColor="{Binding SettingsSubViewModel.Scene.PrimaryBackgroundColor, Mode=TwoWay}" />
+                            </StackPanel>
+                            <StackPanel Orientation="Horizontal" Spacing="5">
+                                <TextBlock ui:Translator.Key="SECONDARY_BG_COLOR" />
+                                <colorPicker:PortableColorPicker Width="40" Height="20" EnableGradientsTab="False"
+                                                                 SelectedColor="{Binding SettingsSubViewModel.Scene.SecondaryBackgroundColor, Mode=TwoWay}" />
+                            </StackPanel>
+
+                            <Button
+                                Command="{Binding SettingsSubViewModel.Scene.ResetBackgroundCommand}"
+                                d:Content="Reset"
+                                Background="{DynamicResource ThemeAccentBrush}"
+                                ui:Translator.Key="RESET" />
                         </controls:FixedSizeStackPanel>
                     </ScrollViewer>
                 </Grid>