Browse Source

Merge branch 'master' into release

Krzysztof Krysiński 1 week ago
parent
commit
e9c56db1fb

+ 1 - 1
src/Drawie

@@ -1 +1 @@
-Subproject commit 38fe5456e1e82664ece3eb9f742a29ee60744ed5
+Subproject commit 592622a39992789d96d3ebba580eb3c24298c285

+ 13 - 0
src/PixiEditor.ChangeableDocument/DocumentChangeTracker.cs

@@ -38,6 +38,19 @@ public class DocumentChangeTracker : IDisposable
     {
         if (running)
             throw new InvalidOperationException("Something is currently being processed");
+
+        if (activeUpdateableChange != null)
+        {
+            try
+            {
+                activeUpdateableChange.Apply(document, false, out var _);
+            }
+            catch (Exception e)
+            {
+                Trace.WriteLine($"Failed to apply active updateable change {activeUpdateableChange}: {e}");
+            }
+        }
+
         if (disposed)
             return;
         disposed = true;

+ 4 - 1
src/PixiEditor/Models/AnalyticsAPI/AnalyticsPeriodicReporter.cs

@@ -33,7 +33,10 @@ public class AnalyticsPeriodicReporter
         _client = client;
         _performanceReporter = new PeriodicPerformanceReporter(this);
 
-        PixiEditorSettings.Analytics.AnalyticsEnabled.ValueChanged += EnableAnalyticsOnValueChanged;
+        if (PixiEditorSettings.Analytics.AnalyticsEnabled != null)
+        {
+            PixiEditorSettings.Analytics.AnalyticsEnabled.ValueChanged += EnableAnalyticsOnValueChanged;
+        }
     }
 
     public void Start(Guid? sessionId)

+ 14 - 11
src/PixiEditor/Models/Controllers/ClipboardController.cs

@@ -60,7 +60,7 @@ internal static class ClipboardController
     ///     2. Position of the copied area
     ///     3. Layers guid, this is used to duplicate the layer when pasting
     /// </summary>
-    public static async Task CopyToClipboard(DocumentViewModel document)
+    public static async Task CopyToClipboard(DocumentViewModel document, RectD? lastTransform)
     {
         await Clipboard.ClearAsync();
 
@@ -84,16 +84,18 @@ internal static class ClipboardController
             surfaceToCopy = surface.AsT2.Item1;
             copyArea = (RectD)surface.AsT2.Item2;
         }
-        else if (document.TransformViewModel.TransformActive)
+        else if (document.TransformViewModel.TransformActive || lastTransform != null)
         {
+            RectD transform = document.TransformViewModel.TransformActive ? document.TransformViewModel.Corners.AABBBounds
+                : lastTransform.Value;
             var surface =
                 document.TryExtractAreaFromSelected(
-                    (RectI)document.TransformViewModel.Corners.AABBBounds.RoundOutwards());
+                    (RectI)transform.RoundOutwards());
             if (surface.IsT0 || surface.IsT1)
                 return;
 
             surfaceToCopy = surface.AsT2.Item1;
-            copyArea = document.TransformViewModel.Corners.AABBBounds;
+            copyArea = transform;
         }
         else if (document.SelectedStructureMember != null)
         {
@@ -575,18 +577,18 @@ internal static class ClipboardController
         return false;
     }
 
-    private static Bitmap FromPNG(IDataObject data)
+    private static Surface FromPNG(IDataObject data)
     {
         object obj = data.Get("PNG");
         if (obj is byte[] bytes)
         {
-            using MemoryStream stream = new MemoryStream(bytes);
-            return new Bitmap(stream);
+            return Surface.Load(bytes);
         }
 
         if (obj is MemoryStream memoryStream)
         {
-            return new Bitmap(memoryStream);
+            bytes = memoryStream.ToArray();
+            return Surface.Load(bytes);
         }
 
         throw new InvalidDataException("PNG data is not in a supported format.");
@@ -598,7 +600,7 @@ internal static class ClipboardController
     {
         try
         {
-            Bitmap source;
+            Surface source;
             if (data.Contains(ClipboardDataFormats.Png) || data.Contains(ClipboardDataFormats.ImageSlashPng))
             {
                 source = FromPNG(data);
@@ -609,7 +611,7 @@ internal static class ClipboardController
                 return false;
             }
 
-            if (source.Format.Value.IsSkiaSupported())
+            /*if (source.Format.Value.IsSkiaSupported())
             {
                 result = SurfaceHelpers.FromBitmap(source);
             }
@@ -620,8 +622,9 @@ internal static class ClipboardController
                     source.Dpi, source.PixelSize.Width * 4);
 
                 result = SurfaceHelpers.FromBitmap(newFormat);
-            }
+            }*/
 
+            result = source;
             return true;
         }
         catch { }

+ 23 - 2
src/PixiEditor/Models/DocumentModels/Public/DocumentOperationsModule.cs

@@ -76,8 +76,10 @@ internal class DocumentOperationsModule : IDocumentOperations
     /// <summary>
     /// Deletes selected pixels
     /// </summary>
+    /// <param name="frame"></param>
     /// <param name="clearSelection">Should the selection be cleared</param>
-    public void DeleteSelectedPixels(int frame, bool clearSelection = false)
+    /// <param name="lastTransformRect"></param>
+    public void DeleteSelectedPixels(int frame, bool clearSelection = false, RectD? lastTransformRect = null)
     {
         var member = Document.SelectedStructureMember;
         if (Internals.ChangeController.IsBlockingChangeActive || member is null)
@@ -85,11 +87,22 @@ internal class DocumentOperationsModule : IDocumentOperations
 
         Internals.ChangeController.TryStopActiveExecutor();
 
+        VectorPath? selection = Internals.Tracker.Document.Selection?.SelectionPath ?? null;
+
+        if (selection == null || selection.IsEmpty)
+        {
+            selection = new VectorPath();
+            if (lastTransformRect is not null)
+            {
+                selection.AddRect(lastTransformRect.Value);
+            }
+        }
+
         bool drawOnMask = member is not ILayerHandler layer || layer.ShouldDrawOnMask;
         if (drawOnMask && !member.HasMaskBindable)
             return;
         Internals.ActionAccumulator.AddActions(new ClearSelectedArea_Action(member.Id,
-            Internals.Tracker.Document.Selection.SelectionPath, drawOnMask, frame));
+            selection, drawOnMask, frame));
         if (clearSelection)
             Internals.ActionAccumulator.AddActions(new ClearSelection_Action());
         Internals.ActionAccumulator.AddFinishedActions();
@@ -1019,4 +1032,12 @@ internal class DocumentOperationsModule : IDocumentOperations
         Internals.ActionAccumulator.AddFinishedActions(
             new ExtractSelectedText_Action(memberId, startIndex, endIndex, extractEachCharacter));
     }
+
+    public void TryStopActiveExecutor()
+    {
+        if (Internals.ChangeController.IsBlockingChangeActive)
+            return;
+
+        Internals.ChangeController.TryStopActiveExecutor();
+    }
 }

+ 1 - 1
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/PasteImageExecutor.cs

@@ -97,7 +97,7 @@ internal class PasteImageExecutor : UpdateableChangeExecutor, ITransformableExec
     public override void ForceStop()
     {
         document!.TransformHandler.HideTransform();
-        internals!.ActionAccumulator.AddActions(new EndPasteImage_Action());
+        internals!.ActionAccumulator.AddFinishedActions(new EndPasteImage_Action());
     }
 
     public bool IsFeatureEnabled<T>()

+ 2 - 2
src/PixiEditor/Properties/AssemblyInfo.cs

@@ -43,5 +43,5 @@ using System.Runtime.InteropServices;
 // You can specify all the values or you can default the Build and Revision Numbers
 // by using the '*' as shown below:
 // [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("2.0.1.1")]
-[assembly: AssemblyFileVersion("2.0.1.1")]
+[assembly: AssemblyVersion("2.0.1.2")]
+[assembly: AssemblyFileVersion("2.0.1.2")]

+ 15 - 8
src/PixiEditor/ViewModels/Document/DocumentViewModel.cs

@@ -852,7 +852,7 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
         if (finalBounds.IsZeroOrNegativeArea)
             return new None();
 
-        Surface output = new(finalBounds.Size);
+        Surface output = Surface.ForDisplay(finalBounds.Size);
 
         VectorPath clipPath = new VectorPath(SelectionPathBindable) { FillType = PathFillType.EvenOdd };
         //clipPath.Transform(Matrix3X3.CreateTranslation(-bounds.X, -bounds.Y));
@@ -1302,13 +1302,20 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
 
     public void Dispose()
     {
-        NodeGraph.Dispose();
-        Renderer.Dispose();
-        SceneRenderer.Dispose();
-        AnimationDataViewModel.Dispose();
-        Internals.ChangeController.TryStopActiveExecutor();
-        Internals.Tracker.Dispose();
-        Internals.Tracker.Document.Dispose();
+        try
+        {
+            NodeGraph.Dispose();
+            Renderer.Dispose();
+            SceneRenderer.Dispose();
+            AnimationDataViewModel.Dispose();
+            Internals.ChangeController.TryStopActiveExecutor();
+            Internals.Tracker.Dispose();
+            Internals.Tracker.Document.Dispose();
+        }
+        catch (Exception ex)
+        {
+            CrashHelper.SendExceptionInfo(ex);
+        }
     }
 
     public VecI GetRenderOutputSize(string renderOutputName)

+ 22 - 4
src/PixiEditor/ViewModels/SubViewModels/ClipboardViewModel.cs

@@ -61,7 +61,7 @@ internal class ClipboardViewModel : SubViewModel<ViewModelMain>
         };
     }
 
-    [Command.Basic("PixiEditor.Clipboard.Cut", "CUT", "CUT_DESCRIPTIVE", CanExecute = "PixiEditor.Selection.IsNotEmpty",
+    [Command.Basic("PixiEditor.Clipboard.Cut", "CUT", "CUT_DESCRIPTIVE",
         Key = Key.X, Modifiers = KeyModifiers.Control,
         MenuItemPath = "EDIT/CUT", MenuItemOrder = 2, Icon = PixiPerfectIcons.Scissors, AnalyticsTrack = true)]
     public async Task Cut()
@@ -69,8 +69,15 @@ internal class ClipboardViewModel : SubViewModel<ViewModelMain>
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
         if (doc is null)
             return;
-        await Copy();
-        doc.Operations.DeleteSelectedPixels(doc.AnimationDataViewModel.ActiveFrameBindable, true);
+
+        var transformActive = doc.TransformViewModel.TransformActive;
+        RectD? lastTransformRect = transformActive
+            ? doc.TransformViewModel.Corners.AABBBounds
+            : null;
+
+        doc.Operations.TryStopActiveExecutor();
+        await Copy(lastTransformRect);
+        doc.Operations.DeleteSelectedPixels(doc.AnimationDataViewModel.ActiveFrameBindable, true, lastTransformRect);
     }
 
     [Command.Basic("PixiEditor.Clipboard.PasteAsNewLayer", true, "PASTE_AS_NEW_LAYER", "PASTE_AS_NEW_LAYER_DESCRIPTIVE",
@@ -343,7 +350,18 @@ internal class ClipboardViewModel : SubViewModel<ViewModelMain>
         if (doc is null)
             return;
 
-        await ClipboardController.CopyToClipboard(doc);
+        await ClipboardController.CopyToClipboard(doc, null);
+
+        SetHasImageInClipboard();
+    }
+
+    private async Task Copy(RectD? lastTransformRect)
+    {
+        var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
+        if (doc is null)
+            return;
+
+        await ClipboardController.CopyToClipboard(doc, lastTransformRect);
 
         SetHasImageInClipboard();
     }

+ 4 - 1
src/PixiEditor/Views/Main/MainTitleBar.axaml

@@ -20,8 +20,11 @@
         <menu:MenuBarViewModel />
     </Design.DataContext>
     <Panel>
-        <Panel Background="{DynamicResource ThemeBackgroundBrush1}" IsHitTestVisible="False"/>
+        <Panel Background="{DynamicResource ThemeBackgroundBrush1}" IsHitTestVisible="False" />
         <DockPanel>
+            <DockPanel.Background>
+                <OnPlatform Default="{x:Null}" Linux="Transparent"/>
+            </DockPanel.Background>
             <dialogs:DialogTitleBar
                 DockPanel.Dock="Right">
                 <dialogs:DialogTitleBar.AdditionalElement>

+ 9 - 2
src/PixiEditor/Views/MainWindow.axaml.cs

@@ -81,8 +81,15 @@ internal partial class MainWindow : Window
         
         StartupPerformance.ReportToMainViewModel();
 
-        var analytics = services.GetService<AnalyticsPeriodicReporter>();
-        analytics?.Start(analyticsSessionId);
+        try
+        {
+            var analytics = services.GetService<AnalyticsPeriodicReporter>();
+            analytics?.Start(analyticsSessionId);
+        }
+        catch (Exception e)
+        {
+            CrashHelper.SendExceptionInfo(e);
+        }
 
         InitializeComponent();
     }