Browse Source

Added toggle snapping to view options

flabbet 9 months ago
parent
commit
46dff82980

+ 1 - 0
src/PixiEditor.UI.Common/Fonts/PixiPerfectIcons.axaml

@@ -57,6 +57,7 @@
             <system:String x:Key="icon-folder">&#xE92D;</system:String>
             <system:String x:Key="icon-globe">&#xE92E;</system:String>
             <system:String x:Key="icon-grid">&#xE92F;</system:String>
+            <system:String x:Key="icon-gridlines">&#xE941;</system:String>
             <system:String x:Key="icon-home">&#xE930;</system:String>
             <system:String x:Key="icon-image-90">&#xE931;</system:String>
             <system:String x:Key="icon-image">&#xE932;</system:String>

+ 7 - 5
src/PixiEditor.UI.Common/Fonts/PixiPerfectIcons.axaml.cs

@@ -60,7 +60,8 @@ public static class PixiPerfectIcons
     public const string FolderPlus = "\ue92c";
     public const string Folder = "\ue92d";
     public const string Globe = "\ue92e";
-    public const string Grid = "\uE941";
+    public const string Grid = "\uE92F";
+    public const string GridLines = "\uE941";
     public const string Home = "\ue930";
     public const string RotateImageMinus90 = "\ue931";
     public const string Image = "\ue932";
@@ -130,10 +131,11 @@ public static class PixiPerfectIcons
     public const string Reset = "R"; // TODO: Create a reset icon
     public const string ToggleLayerVisible = "\u25a1;"; // TODO: Create a toggle layer visible icon
     public const string ToggleMask = "\u25a1;"; // TODO: Create a toggle mask icon
-    public static string Pen => "\uE971";
-    public static string LowResCircle => "\uE986";
-    public static string LowResSquare => "\uE988";
-    public static string LowResLine => "\uE989";
+    public const string Pen = "\uE971";
+    public const string LowResCircle = "\uE986";
+    public const string Snapping = "\ue987";
+    public const string LowResSquare = "\uE988";
+    public const string LowResLine = "\uE989";
 
     public static Stream GetFontStream()
     {

+ 2 - 1
src/PixiEditor/Data/Localization/Languages/en.json

@@ -759,5 +759,6 @@
   "HARDNESS_SETTING": "Hardness",
   "SPACING_SETTING": "Spacing",
   "ANTI_ALIASING_SETTING": "Anti-aliasing",
-  "TOLERANCE_LABEL": "Tolerance"
+  "TOLERANCE_LABEL": "Tolerance",
+  "TOGGLE_SNAPPING": "Toggle snapping"
 }

+ 2 - 1
src/PixiEditor/Helpers/ServiceCollectionHelpers.cs

@@ -185,7 +185,8 @@ internal static class ServiceCollectionHelpers
             .AddSingleton<MenuItemBuilder, FileExitMenuBuilder>()
             .AddSingleton<MenuItemBuilder, SymmetryMenuBuilder>()
             .AddSingleton<MenuItemBuilder, OpenDockablesMenuBuilder>()
-            .AddSingleton<MenuItemBuilder, ToggleGridLinesMenuBuilder>();
+            .AddSingleton<MenuItemBuilder, ToggleGridLinesMenuBuilder>()
+            .AddSingleton<MenuItemBuilder, ToggleSnappingMenuBuilder>();
     }
 
     public static IServiceCollection AddExtensionServices(this IServiceCollection collection, ExtensionLoader loader) =>

+ 5 - 12
src/PixiEditor/ViewModels/Document/SnappingViewModel.cs

@@ -1,7 +1,10 @@
-using CommunityToolkit.Mvvm.ComponentModel;
+using System.Drawing;
+using CommunityToolkit.Mvvm.ComponentModel;
 using PixiEditor.Models.Controllers.InputDevice;
 using PixiEditor.Models.Handlers;
 using Drawie.Numerics;
+using PixiEditor.Models.Commands.Attributes.Commands;
+using PixiEditor.UI.Common.Fonts;
 
 namespace PixiEditor.ViewModels.Document;
 
@@ -10,21 +13,11 @@ public class SnappingViewModel : PixiObservableObject, ISnappingHandler
     private bool snappingEnabled = true;
     public SnappingController SnappingController { get; } = new SnappingController();
 
-    public bool SnappingEnabled
-    {
-        get => snappingEnabled;
-        set
-        {
-            SetProperty(ref snappingEnabled, value);
-            SnappingController.SnappingEnabled = value;
-        }
-    }
-
     public SnappingViewModel()
     {
         SnappingController.AddXYAxis("Root", VecD.Zero);
     }
-
+    
     public void AddFromDocumentSize(VecD documentSize)
     {
         SnappingController.AddXYAxis("DocumentSize", documentSize);

+ 1 - 1
src/PixiEditor/ViewModels/Menu/MenuBuilders/ToggleGridLinesMenuBuilder.cs

@@ -18,7 +18,7 @@ internal class ToggleGridLinesMenuBuilder : MenuItemBuilder
             Translator.SetKey(gridLinesItem, "TOGGLE_GRIDLINES");
             gridLinesItem.Icon = new Image()
             {
-                Source = PixiPerfectIcons.ToIcon(PixiPerfectIcons.Grid),
+                Source = PixiPerfectIcons.ToIcon(PixiPerfectIcons.GridLines),
                 Width = Models.Commands.XAML.Menu.IconDimensions,
                 Height = Models.Commands.XAML.Menu.IconDimensions
             };

+ 43 - 0
src/PixiEditor/ViewModels/Menu/MenuBuilders/ToggleSnappingMenuBuilder.cs

@@ -0,0 +1,43 @@
+using Avalonia.Controls;
+using Avalonia.Data;
+using Avalonia.Input;
+using PixiEditor.Extensions.UI;
+using PixiEditor.UI.Common.Controls;
+using PixiEditor.UI.Common.Fonts;
+
+namespace PixiEditor.ViewModels.Menu.MenuBuilders;
+
+internal class ToggleSnappingMenuBuilder : MenuItemBuilder
+{
+    public override void ModifyMenuTree(ICollection<MenuItem> tree)
+    {
+        if (TryFindMenuItem(tree, "VIEW", out MenuItem? viewItem))
+        {
+            ToggleableMenuItem snappingItem = new ToggleableMenuItem();
+            Translator.SetKey(snappingItem, "TOGGLE_SNAPPING");
+            snappingItem.Icon = new Image()
+            {
+                Source = PixiPerfectIcons.ToIcon(PixiPerfectIcons.Snapping),
+                Width = Models.Commands.XAML.Menu.IconDimensions,
+                Height = Models.Commands.XAML.Menu.IconDimensions
+            };
+
+            BindItem(snappingItem);
+            viewItem.Items.Add(snappingItem);
+        }
+    }
+
+    private void BindItem(ToggleableMenuItem gridLinesItem)
+    {
+        gridLinesItem.Bind(ToggleableMenuItem.IsCheckedProperty, new Binding("ViewportSubViewModel.SnappingEnabled")
+        {
+            Source = ViewModelMain.Current,
+            Mode = BindingMode.TwoWay
+        });
+
+        gridLinesItem.Bind(InputElement.IsEnabledProperty, new Binding("!!DocumentManagerSubViewModel.ActiveDocument")
+        {
+            Source = ViewModelMain.Current
+        });
+    }
+}

+ 24 - 3
src/PixiEditor/ViewModels/SubViewModels/ViewOptionsViewModel.cs

@@ -13,22 +13,36 @@ internal class ViewOptionsViewModel : SubViewModel<ViewModelMain>
         get => gridLinesEnabled;
         set => SetProperty(ref gridLinesEnabled, value);
     }
+    
+    private bool snappingEnabled = true;
+    public bool SnappingEnabled
+    {
+        get => snappingEnabled;
+        set
+        {
+            SetProperty(ref snappingEnabled, value);
+            Owner.DocumentManagerSubViewModel.ActiveDocument.SnappingViewModel.SnappingController.SnappingEnabled = value;
+        }
+    }
 
     public ViewOptionsViewModel(ViewModelMain owner)
         : base(owner)
     {
     }
 
-    [Command.Basic("PixiEditor.View.ToggleGrid", "TOGGLE_GRIDLINES", "TOGGLE_GRIDLINES", Key = Key.OemTilde, Modifiers = KeyModifiers.Control,
+    [Command.Basic("PixiEditor.View.ToggleGrid", "TOGGLE_GRIDLINES", "TOGGLE_GRIDLINES", Key = Key.OemTilde,
+        Modifiers = KeyModifiers.Control,
         Icon = PixiPerfectIcons.Grid)]
     public void ToggleGridLines()
     {
         GridLinesEnabled = !GridLinesEnabled;
     }
 
-    [Command.Basic("PixiEditor.View.ZoomIn", 1, "ZOOM_IN", "ZOOM_IN", CanExecute = "PixiEditor.HasDocument", Key = Key.OemPlus,
+    [Command.Basic("PixiEditor.View.ZoomIn", 1, "ZOOM_IN", "ZOOM_IN", CanExecute = "PixiEditor.HasDocument",
+        Key = Key.OemPlus,
         Icon = PixiPerfectIcons.ZoomIn, AnalyticsTrack = true)]
-    [Command.Basic("PixiEditor.View.Zoomout", -1, "ZOOM_OUT", "ZOOM_OUT", CanExecute = "PixiEditor.HasDocument", Key = Key.OemMinus,
+    [Command.Basic("PixiEditor.View.Zoomout", -1, "ZOOM_OUT", "ZOOM_OUT", CanExecute = "PixiEditor.HasDocument",
+        Key = Key.OemMinus,
         Icon = PixiPerfectIcons.ZoomOut, AnalyticsTrack = true)]
     public void ZoomViewport(double zoom)
     {
@@ -37,4 +51,11 @@ internal class ViewOptionsViewModel : SubViewModel<ViewModelMain>
             return;
         viewport.ZoomViewportTrigger.Execute(this, zoom);
     }
+
+    [Command.Basic("PixiEditor.ToggleSnapping", "TOGGLE_SNAPPING", "TOGGLE_SNAPPING",
+        Icon = PixiPerfectIcons.Snapping)]
+    public void ToggleSnapping()
+    {
+        SnappingEnabled = !SnappingEnabled;
+    }
 }

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

@@ -35,6 +35,7 @@
         FlipY="{Binding FlipY, Mode=TwoWay}"
         Channels="{Binding Channels, Mode=TwoWay}"
         SnappingViewModel="{Binding ActiveDocument.SnappingViewModel, Source={viewModels1:MainVM DocumentManagerSVM}}"
+        SnappingEnabled="{Binding ViewportSubViewModel.SnappingEnabled, Source={viewModels1:MainVM}, Mode=TwoWay}"
         ContextRequested="Viewport_OnContextMenuOpening"
         Document="{Binding Document}">
         <viewportControls:Viewport.ContextFlyout>

+ 13 - 5
src/PixiEditor/Views/Main/ViewportControls/Viewport.axaml

@@ -124,11 +124,19 @@
                                           Cursor="Hand" />
                         </StackPanel>
                         <Separator />
-                        <ToggleButton Margin="10 0 0 0" Width="32" Height="32"
-                                      ui:Translator.TooltipKey="TOGGLE_SNAPPING"
-                                      Classes="OverlayToggleButton pixi-icon"
-                                      Content="{DynamicResource icon-snapping}"
-                                      IsChecked="{Binding SnappingViewModel.SnappingEnabled, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}" />
+                        <StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
+                            <ToggleButton Margin="10 0 0 0" Width="32" Height="32"
+                                          ui:Translator.TooltipKey="TOGGLE_SNAPPING"
+                                          Classes="OverlayToggleButton pixi-icon"
+                                          Content="{DynamicResource icon-gridlines}"
+                                          IsChecked="{Binding GridLinesVisible, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}" />
+                            
+                            <ToggleButton Margin="10 0 0 0" Width="32" Height="32"
+                                          ui:Translator.TooltipKey="TOGGLE_SNAPPING"
+                                          Classes="OverlayToggleButton pixi-icon"
+                                          Content="{DynamicResource icon-snapping}"
+                                          IsChecked="{Binding SnappingEnabled, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}" />
+                        </StackPanel>
                     </StackPanel>
                 </Border>
             </overlays:TogglableFlyout.Child>

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

@@ -290,6 +290,7 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
 
     private MouseUpdateController? mouseUpdateController;
     private ViewportOverlays builtInOverlays = new();
+    public static readonly StyledProperty<bool> SnappingEnabledProperty = AvaloniaProperty.Register<Viewport, bool>("SnappingEnabled");
 
     static Viewport()
     {
@@ -337,6 +338,12 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
         set { SetValue(HighResPreviewProperty, value); }
     }
 
+    public bool SnappingEnabled
+    {
+        get { return (bool)GetValue(SnappingEnabledProperty); }
+        set { SetValue(SnappingEnabledProperty, value); }
+    }
+
     private void ForceRefreshFinalImage()
     {
         Scene.InvalidateVisual();