Browse Source

Merge branch 'avalonia-rewrite' of https://github.com/PixiEditor/PixiEditor into avalonia-rewrite

Krzysztof Krysiński 1 year ago
parent
commit
9a48bc0870
24 changed files with 102 additions and 35 deletions
  1. 1 1
      src/Directory.Build.props
  2. 17 4
      src/PixiEditor.AvaloniaUI/Helpers/Converters/ImagePathToBitmapConverter.cs
  3. BIN
      src/PixiEditor.AvaloniaUI/Images/Tools/PreciseCursor.png
  4. 9 0
      src/PixiEditor.AvaloniaUI/Models/Handlers/IToolHandler.cs
  5. 7 3
      src/PixiEditor.AvaloniaUI/Models/IO/Importer.cs
  6. 13 0
      src/PixiEditor.AvaloniaUI/Models/Input/Cursors.cs
  7. 7 7
      src/PixiEditor.AvaloniaUI/Models/UserData/RecentlyOpenedDocument.cs
  8. 1 0
      src/PixiEditor.AvaloniaUI/ViewModels/Document/DocumentViewModel.cs
  9. 2 2
      src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/IoViewModel.cs
  10. 12 0
      src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/ToolsViewModel.cs
  11. 5 4
      src/PixiEditor.AvaloniaUI/ViewModels/Tools/ToolViewModel.cs
  12. 0 1
      src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/MoveToolViewModel.cs
  13. 2 0
      src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/MoveViewportToolViewModel.cs
  14. 3 1
      src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/PenToolViewModel.cs
  15. 1 0
      src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/RotateViewportToolViewModel.cs
  16. 2 0
      src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/ZoomToolViewModel.cs
  17. 1 1
      src/PixiEditor.AvaloniaUI/Views/Main/Viewport.axaml
  18. 3 2
      src/PixiEditor.AvaloniaUI/Views/Main/Viewport.axaml.cs
  19. 1 0
      src/PixiEditor.AvaloniaUI/Views/Overlays/TransformOverlay/TransformOverlay.cs
  20. 1 1
      src/PixiEditor.DrawingApi.Skia/PixiEditor.DrawingApi.Skia.csproj
  21. 1 1
      src/PixiEditor.Tests/PixiEditor.Tests.csproj
  22. 1 1
      src/PixiEditor.Zoombox/Operations/RotateDragOperation.cs
  23. 2 2
      src/PixiEditor.Zoombox/Zoombox.axaml
  24. 10 4
      src/PixiEditor.Zoombox/Zoombox.axaml.cs

+ 1 - 1
src/Directory.Build.props

@@ -1,7 +1,7 @@
 <Project>
     <PropertyGroup>
         <CodeAnalysisRuleSet>../Custom.ruleset</CodeAnalysisRuleSet>
-		<AvaloniaVersion>11.0.5</AvaloniaVersion>
+		<AvaloniaVersion>11.0.6</AvaloniaVersion>
     </PropertyGroup>
     <ItemGroup>
         <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />

+ 17 - 4
src/PixiEditor.AvaloniaUI/Helpers/Converters/ImagePathToBitmapConverter.cs

@@ -1,4 +1,5 @@
 using System.Globalization;
+using System.IO;
 using System.Reflection;
 using Avalonia;
 using Avalonia.Data;
@@ -13,11 +14,23 @@ internal class ImagePathToBitmapConverter : SingleInstanceConverter<ImagePathToB
     {
         if (value is not string path)
             return AvaloniaProperty.UnsetValue;
-        
+
+        try
+        {
+            return LoadBitmapFromRelativePath(path);
+        }
+        catch (FileNotFoundException)
+        {
+            return AvaloniaProperty.UnsetValue;
+        }
+    }
+
+    public static Bitmap LoadBitmapFromRelativePath(string path)
+    {
         Uri uri = new($"avares://{Assembly.GetExecutingAssembly().FullName}{path}");
         if (!AssetLoader.Exists(uri))
-            return AvaloniaProperty.UnsetValue;
-        
-        return new Bitmap(AssetLoader.Open(uri));
+            throw new FileNotFoundException($"Could not find asset with path {path}");
+
+        return new Bitmap(AssetLoader.Open(uri)).CreateScaledBitmap(new PixelSize(32, 32));
     }
 }

BIN
src/PixiEditor.AvaloniaUI/Images/Tools/PreciseCursor.png


+ 9 - 0
src/PixiEditor.AvaloniaUI/Models/Handlers/IToolHandler.cs

@@ -30,6 +30,15 @@ internal interface IToolHandler : IHandler
     /// </summary>
     public virtual bool IsErasable => false;
 
+    /// <summary>
+    /// Indicates whether active linked tool stops on use.
+    /// </summary>
+    /// <remarks>
+    /// If this property is true, the linked tool will stop executing when used.
+    /// If this property is false, the linked tool will continue executing even after being used.
+    /// </remarks>
+    public virtual bool StopsLinkedToolOnUse => true;
+
     /// <summary>
     /// The mouse button that is being used with the tool
     /// </summary>

+ 7 - 3
src/PixiEditor.AvaloniaUI/Models/IO/Importer.cs

@@ -43,12 +43,11 @@ internal class Importer : ObservableObject
         return resized;
     }
 
-    public static WriteableBitmap ImportWriteableBitmap(string path)
+    public static Bitmap ImportBitmap(string path)
     {
         try
         {
-            Uri uri = new Uri(path, UriKind.RelativeOrAbsolute);
-            return new Bitmap(path).ToWriteableBitmap();
+            return new Bitmap(path);
         }
         catch (NotSupportedException e)
         {
@@ -64,6 +63,11 @@ internal class Importer : ObservableObject
         }
     }
 
+    public static WriteableBitmap ImportWriteableBitmap(string path)
+    {
+        return ImportBitmap(path).ToWriteableBitmap();
+    }
+
     public static DocumentViewModel ImportDocument(string path, bool associatePath = true)
     {
         try

+ 13 - 0
src/PixiEditor.AvaloniaUI/Models/Input/Cursors.cs

@@ -0,0 +1,13 @@
+using Avalonia;
+using Avalonia.Input;
+using Avalonia.Media.Imaging;
+using PixiEditor.AvaloniaUI.Helpers.Converters;
+
+namespace PixiEditor.AvaloniaUI.Models.Input;
+
+public static class Cursors
+{
+    public static Cursor PreciseCursor { get; } = new Cursor(
+        ImagePathToBitmapConverter.LoadBitmapFromRelativePath("/Images/Tools/PreciseCursor.png"),
+        new PixelPoint(16, 16));
+}

+ 7 - 7
src/PixiEditor.AvaloniaUI/Models/UserData/RecentlyOpenedDocument.cs

@@ -6,6 +6,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
 using PixiEditor.AvaloniaUI.Exceptions;
 using PixiEditor.AvaloniaUI.Helpers;
 using PixiEditor.AvaloniaUI.Helpers.Extensions;
+using PixiEditor.AvaloniaUI.Models.IO;
 using PixiEditor.Parser;
 using PixiEditor.Parser.Deprecated;
 
@@ -18,7 +19,7 @@ internal class RecentlyOpenedDocument : ObservableObject
 
     private string filePath;
 
-    private WriteableBitmap previewBitmap;
+    private Bitmap previewBitmap;
 
     public string FilePath
     {
@@ -59,7 +60,7 @@ internal class RecentlyOpenedDocument : ObservableObject
         }
     }
 
-    public WriteableBitmap PreviewBitmap
+    public Bitmap PreviewBitmap
     {
         get
         {
@@ -78,7 +79,7 @@ internal class RecentlyOpenedDocument : ObservableObject
         FilePath = path;
     }
 
-    private WriteableBitmap LoadPreviewBitmap()
+    private Bitmap LoadPreviewBitmap()
     {
         if (!File.Exists(FilePath))
         {
@@ -128,12 +129,11 @@ internal class RecentlyOpenedDocument : ObservableObject
 
         if (SupportedFilesHelper.IsExtensionSupported(FileExtension))
         {
-            WriteableBitmap bitmap = null;
+            Bitmap bitmap = null;
 
             try
             {
-                //TODO: Fix this
-                //bitmap = Importer.ImportWriteableBitmap(FilePath);
+                bitmap = Importer.ImportBitmap(FilePath);
             }
             catch (RecoverableException)
             {
@@ -150,7 +150,7 @@ internal class RecentlyOpenedDocument : ObservableObject
         return null;
     }
 
-    private WriteableBitmap DownscaleToMaxSize(WriteableBitmap bitmap)
+    private Bitmap DownscaleToMaxSize(Bitmap bitmap)
     {
         if (bitmap.PixelSize.Width > Constants.MaxPreviewWidth || bitmap.PixelSize.Height > Constants.MaxPreviewHeight)
         {

+ 1 - 0
src/PixiEditor.AvaloniaUI/ViewModels/Document/DocumentViewModel.cs

@@ -123,6 +123,7 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
     private readonly HashSet<StructureMemberViewModel> softSelectedStructureMembers = new();
 
     public bool UpdateableChangeActive => Internals.ChangeController.IsChangeActive;
+    public bool PointerDragChangeInProgress => Internals.ChangeController.IsChangeActive && Internals.ChangeController.LeftMousePressed;
     public bool HasSavedUndo => Internals.Tracker.HasSavedUndo;
     public bool HasSavedRedo => Internals.Tracker.HasSavedRedo;
 

+ 2 - 2
src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/IoViewModel.cs

@@ -39,7 +39,7 @@ internal class IoViewModel : SubViewModel<ViewModelMain>
         MouseDownCommand = new RelayCommand<MouseOnCanvasEventArgs>(mouseFilter.MouseDownInlet);
         MouseMoveCommand = new RelayCommand<MouseOnCanvasEventArgs>(mouseFilter.MouseMoveInlet);
         MouseUpCommand = new RelayCommand<MouseOnCanvasEventArgs>(mouseFilter.MouseUpInlet);
-        PreviewMouseMiddleButtonCommand = new RelayCommand(OnPreviewMiddleMouseButton);
+        PreviewMouseMiddleButtonCommand = new RelayCommand(OnMiddleMouseButton);
         // TODO: Implement mouse capturing
         //GlobalMouseHook.Instance.OnMouseUp += mouseFilter.MouseUpInlet;
 
@@ -207,7 +207,7 @@ internal class IoViewModel : SubViewModel<ViewModelMain>
         tools.SetActiveTool<EraserToolViewModel>(true);
     }
     
-    private void OnPreviewMiddleMouseButton()
+    private void OnMiddleMouseButton()
     {
         Owner.ToolsSubViewModel.SetActiveTool<MoveViewportToolViewModel>(true);
     }

+ 12 - 0
src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/ToolsViewModel.cs

@@ -1,8 +1,13 @@
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using System.IO;
 using System.Linq;
+using System.Reflection;
+using Avalonia;
 using Avalonia.Input;
+using Avalonia.Media.Imaging;
 using Microsoft.Extensions.DependencyInjection;
+using PixiEditor.AvaloniaUI.Helpers.Converters;
 using PixiEditor.AvaloniaUI.Models.Commands.Attributes.Commands;
 using PixiEditor.AvaloniaUI.Models.Commands.Attributes.Evaluators;
 using PixiEditor.AvaloniaUI.Models.Controllers;
@@ -136,6 +141,8 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
 
     public void SetActiveTool(IToolHandler tool, bool transient)
     {
+        if(Owner.DocumentManagerSubViewModel.ActiveDocument is { PointerDragChangeInProgress: true }) return;
+
         if (ActiveTool == tool)
         {
             ActiveTool.IsTransient = transient;
@@ -268,6 +275,11 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
         if (ActiveTool == null) return;
 
         ActiveTool.UsedWith = button;
+        if (ActiveTool.StopsLinkedToolOnUse)
+        {
+            ViewModelMain.Current.DocumentManagerSubViewModel.ActiveDocument?.Operations.TryStopToolLinkedExecutor();
+        }
+
         ActiveTool.UseTool(canvasPos);
     }
 

+ 5 - 4
src/PixiEditor.AvaloniaUI/ViewModels/Tools/ToolViewModel.cs

@@ -40,6 +40,9 @@ internal abstract class ToolViewModel : ObservableObject, IToolHandler
    /// </summary>
     public virtual bool IsErasable => false;
 
+   /// <inheritdoc cref="IToolHandler.StopsLinkedToolOnUse"/>
+    public virtual bool StopsLinkedToolOnUse => true;
+
     /// <summary>
     /// The mouse button that is being used with the tool
     /// </summary>
@@ -82,11 +85,9 @@ internal abstract class ToolViewModel : ObservableObject, IToolHandler
     }
 
     public virtual void ModifierKeyChanged(bool ctrlIsDown, bool shiftIsDown, bool altIsDown) { }
+
     public virtual void UseTool(VecD pos) { }
-    public virtual void OnSelected() 
-    {
-        ViewModelMain.Current.DocumentManagerSubViewModel.ActiveDocument?.Operations.TryStopToolLinkedExecutor();
-    }
+    public virtual void OnSelected() { }
 
     public virtual void OnDeselecting()
     { }

+ 0 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/MoveToolViewModel.cs

@@ -16,7 +16,6 @@ internal class MoveToolViewModel : ToolViewModel, IMoveToolHandler
 
     private string transformingActionDisplay = "MOVE_TOOL_ACTION_DISPLAY_TRANSFORMING";
     private bool transformingSelectedArea = false;
-
     public bool MoveAllLayers { get; set; }
 
     public MoveToolViewModel()

+ 2 - 0
src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/MoveViewportToolViewModel.cs

@@ -13,6 +13,8 @@ internal class MoveViewportToolViewModel : ToolViewModel
     public override bool HideHighlight => true;
     public override LocalizedString Tooltip => new LocalizedString("MOVE_VIEWPORT_TOOLTIP", Shortcut);
 
+    public override bool StopsLinkedToolOnUse => false;
+
     public MoveViewportToolViewModel()
     {
         Cursor = new Cursor(StandardCursorType.SizeAll);

+ 3 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/PenToolViewModel.cs

@@ -1,7 +1,9 @@
 using System.Linq;
 using Avalonia.Input;
+using Avalonia.Media.Imaging;
 using PixiEditor.AvaloniaUI.Models.Commands.Attributes.Commands;
 using PixiEditor.AvaloniaUI.Models.Handlers.Tools;
+using PixiEditor.AvaloniaUI.Models.Input;
 using PixiEditor.AvaloniaUI.ViewModels.Tools.ToolSettings.Settings;
 using PixiEditor.AvaloniaUI.ViewModels.Tools.ToolSettings.Toolbars;
 using PixiEditor.AvaloniaUI.Views.Overlays.BrushShapeOverlay;
@@ -21,7 +23,7 @@ namespace PixiEditor.AvaloniaUI.ViewModels.Tools.Tools
 
         public PenToolViewModel()
         {
-            Cursor = new Cursor(StandardCursorType.Help); // TODO: Create pen cursor
+            Cursor = Cursors.PreciseCursor;
             Toolbar = ToolbarFactory.Create<PenToolViewModel, BasicToolbar>(this);
             
             ViewModelMain.Current.ToolsSubViewModel.SelectedToolChanged += SelectedToolChanged;

+ 1 - 0
src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/RotateViewportToolViewModel.cs

@@ -11,6 +11,7 @@ internal class RotateViewportToolViewModel : ToolViewModel
     public override string ToolNameLocalizationKey => "ROTATE_VIEWPORT_TOOL";
     public override BrushShape BrushShape => BrushShape.Hidden;
     public override bool HideHighlight => true;
+    public override bool StopsLinkedToolOnUse => false;
     public override LocalizedString Tooltip => new LocalizedString("ROTATE_VIEWPORT_TOOLTIP", Shortcut);
 
     public RotateViewportToolViewModel()

+ 2 - 0
src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/ZoomToolViewModel.cs

@@ -21,6 +21,8 @@ internal class ZoomToolViewModel : ToolViewModel
     public override string ToolNameLocalizationKey => "ZOOM_TOOL";
     public override BrushShape BrushShape => BrushShape.Hidden;
 
+    public override bool StopsLinkedToolOnUse => false;
+
     public ZoomToolViewModel()
     {
         ActionDisplay = defaultActionDisplay;

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

@@ -30,7 +30,7 @@
     d:DesignWidth="800">
     <Grid
         x:Name="viewportGrid"
-        PointerPressed="Image_MouseDown">
+        >
         <Interaction.Behaviors>
             <!--TODO: Implement stylus support-->
             <!--<EventTriggerBehavior EventName="StylusButtonDown">

+ 3 - 2
src/PixiEditor.AvaloniaUI/Views/Main/Viewport.axaml.cs

@@ -310,8 +310,9 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
         Unloaded += OnUnload;
 
         //TODO: It's weird that I had to do it this way, right click didn't raise Image_MouseUp otherwise.
-        this.AddHandler(PointerReleasedEvent, Image_MouseUp, RoutingStrategies.Tunnel);
-        
+        viewportGrid.AddHandler(PointerReleasedEvent, Image_MouseUp, RoutingStrategies.Tunnel);
+        viewportGrid.AddHandler(PointerPressedEvent, Image_MouseDown, RoutingStrategies.Bubble);
+
         mouseUpdateController = new MouseUpdateController(this, Image_MouseMove);
     }
 

+ 1 - 0
src/PixiEditor.AvaloniaUI/Views/Overlays/TransformOverlay/TransformOverlay.cs

@@ -362,6 +362,7 @@ internal class TransformOverlay : Overlay
         }
         
         e.Pointer.Capture(this);
+        e.Handled = true;
     }
 
     protected override void OnPointerMoved(PointerEventArgs e)

+ 1 - 1
src/PixiEditor.DrawingApi.Skia/PixiEditor.DrawingApi.Skia.csproj

@@ -61,7 +61,7 @@
     </PropertyGroup>
 
     <ItemGroup>
-      <PackageReference Include="SkiaSharp" Version="2.80.3" />
+      <PackageReference Include="SkiaSharp" Version="2.88.6" />
     </ItemGroup>
 
     <ItemGroup>

+ 1 - 1
src/PixiEditor.Tests/PixiEditor.Tests.csproj

@@ -11,7 +11,7 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Avalonia" Version="11.0.4" />
+        <PackageReference Include="Avalonia" Version="$(AvaloniaVersion)" />
         <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0"/>
         <PackageReference Include="xunit" Version="2.4.2"/>
         <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">

+ 1 - 1
src/PixiEditor.Zoombox/Operations/RotateDragOperation.cs

@@ -32,7 +32,7 @@ internal class RotateDragOperation : IDragOperation
 
     private double GetAngle(VecD point)
     {
-        VecD center = new(owner.mainCanvas.Width / 2, owner.mainCanvas.Height / 2);
+        VecD center = new(owner.mainCanvas.Bounds.Width / 2, owner.mainCanvas.Bounds.Height / 2);
         double angle = (point - center).Angle;
         if (double.IsNaN(angle) || double.IsInfinity(angle))
             return 0;

+ 2 - 2
src/PixiEditor.Zoombox/Zoombox.axaml

@@ -36,8 +36,8 @@
                         ScaleY="{Binding ElementName=uc, Path=FlipTransformY}" />
                 </TransformGroup>
             </Grid.RenderTransform>
-            <ContentPresenter
-                Content="{Binding AdditionalContent, ElementName=uc}" />
+            <ContentPresenter RenderTransformOrigin="0.5,0.5"
+                              Content="{Binding AdditionalContent, ElementName=uc}" />
         </Grid>
     </Canvas>
 </UserControl>

+ 10 - 4
src/PixiEditor.Zoombox/Zoombox.axaml.cs

@@ -176,9 +176,15 @@ public partial class Zoombox : UserControl, INotifyPropertyChanged
     private static void ZoomModeChanged(AvaloniaPropertyChangedEventArgs<ZoomboxMode> e)
     {
         Zoombox sender = (Zoombox)e.Sender;
-        sender.activeDragOperation?.Terminate();
-        sender.activeDragOperation = null;
-        sender.activeMouseDownEventArgs = null;
+
+        bool reset = sender.activeDragOperation != null;
+
+        if (reset)
+        {
+            sender.activeDragOperation?.Terminate();
+            sender.activeDragOperation = null;
+            sender.activeMouseDownEventArgs = null;
+        }
     }
 
     private double[]? zoomValues;
@@ -292,7 +298,7 @@ public partial class Zoombox : UserControl, INotifyPropertyChanged
         FlipX = false;
         FlipY = false;
         Scale = 1 / scaleFactor;
-        Center = newSize / 2 * scaleFactor;
+        Center = newSize / 2;
     }
 
     public void ZoomIntoCenter(double delta)