Browse Source

Merge branch 'master' into release

Krzysztof Krysiński 1 week ago
parent
commit
6ee3736a30

+ 1 - 1
src/Drawie

@@ -1 +1 @@
-Subproject commit 6844357c31e686ad30f255a4bfab93b37e5bfde8
+Subproject commit 0145d4583f4ac2e20a4658e451e841a9f8397a9e

+ 22 - 8
src/PixiEditor.ChangeableDocument/Changes/Drawing/CombineStructureMembersOnto_Change.cs

@@ -4,6 +4,8 @@ using PixiEditor.ChangeableDocument.Changes.Structure;
 using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.ChangeableDocument.Rendering;
 using Drawie.Backend.Core;
 using Drawie.Backend.Core;
 using Drawie.Backend.Core.Bridge;
 using Drawie.Backend.Core.Bridge;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.ColorsImpl.Paintables;
 using Drawie.Backend.Core.Numerics;
 using Drawie.Backend.Core.Numerics;
 using Drawie.Backend.Core.Vector;
 using Drawie.Backend.Core.Vector;
 using Drawie.Numerics;
 using Drawie.Numerics;
@@ -144,6 +146,11 @@ internal class CombineStructureMembersOnto_Change : Change
 
 
         var ordererd = OrderLayers(layersToCombine, target);
         var ordererd = OrderLayers(layersToCombine, target);
 
 
+        if (ordererd.Count == 0)
+        {
+            return changes;
+        }
+
         foreach (var guid in ordererd)
         foreach (var guid in ordererd)
         {
         {
             var layer = target.FindMemberOrThrow<StructureNode>(guid);
             var layer = target.FindMemberOrThrow<StructureNode>(guid);
@@ -215,6 +222,11 @@ internal class CombineStructureMembersOnto_Change : Change
             }
             }
             else
             else
             {
             {
+                if (targetPath == null)
+                {
+                    targetPath = new VectorPath();
+                }
+
                 targetPath.AddPath(path, vectorNode.EmbeddedShapeData.TransformationMatrix, AddPathMode.Append);
                 targetPath.AddPath(path, vectorNode.EmbeddedShapeData.TransformationMatrix, AddPathMode.Append);
                 path.Dispose();
                 path.Dispose();
             }
             }
@@ -227,10 +239,10 @@ internal class CombineStructureMembersOnto_Change : Change
             ShapeVectorData shape = clone as ShapeVectorData;
             ShapeVectorData shape = clone as ShapeVectorData;
             data = new PathVectorData(targetPath)
             data = new PathVectorData(targetPath)
             {
             {
-                Stroke = shape.Stroke,
-                FillPaintable = shape.FillPaintable,
-                StrokeWidth = shape.StrokeWidth,
-                Fill = shape.Fill,
+                Stroke = shape?.Stroke,
+                FillPaintable = shape?.FillPaintable,
+                StrokeWidth = shape?.StrokeWidth ?? 1,
+                Fill = shape?.Fill ?? true,
                 TransformationMatrix = Matrix3X3.Identity
                 TransformationMatrix = Matrix3X3.Identity
             };
             };
         }
         }
@@ -248,9 +260,9 @@ internal class CombineStructureMembersOnto_Change : Change
 
 
     private AffectedArea RasterMerge(Document target, StructureNode targetLayer, int frame)
     private AffectedArea RasterMerge(Document target, StructureNode targetLayer, int frame)
     {
     {
-        if(targetLayer is not ImageLayerNode)
+        if (targetLayer is not ImageLayerNode)
             throw new InvalidOperationException("Target layer is not a raster layer");
             throw new InvalidOperationException("Target layer is not a raster layer");
-        
+
         var toDrawOnImage = ((ImageLayerNode)targetLayer).GetLayerImageAtFrame(frame);
         var toDrawOnImage = ((ImageLayerNode)targetLayer).GetLayerImageAtFrame(frame);
         toDrawOnImage.EnqueueClear();
         toDrawOnImage.EnqueueClear();
 
 
@@ -261,7 +273,8 @@ internal class CombineStructureMembersOnto_Change : Change
         AffectedArea affArea = new();
         AffectedArea affArea = new();
         DrawingBackendApi.Current.RenderingDispatcher.Invoke(() =>
         DrawingBackendApi.Current.RenderingDispatcher.Invoke(() =>
         {
         {
-            renderer.RenderLayers(tempTexture.DrawingSurface, layersToCombine, frame, ChunkResolution.Full, target.Size);
+            renderer.RenderLayers(tempTexture.DrawingSurface, layersToCombine, frame, ChunkResolution.Full,
+                target.Size);
 
 
             toDrawOnImage.EnqueueDrawTexture(VecI.Zero, tempTexture);
             toDrawOnImage.EnqueueDrawTexture(VecI.Zero, tempTexture);
 
 
@@ -288,7 +301,8 @@ internal class CombineStructureMembersOnto_Change : Change
         return ordered.Reverse().ToHashSet();
         return ordered.Reverse().ToHashSet();
     }
     }
 
 
-    private void AddMissingKeyFrame(StructureNode targetLayer, int frame, StructureNode layer, List<IChangeInfo> changes,
+    private void AddMissingKeyFrame(StructureNode targetLayer, int frame, StructureNode layer,
+        List<IChangeInfo> changes,
         Document target)
         Document target)
     {
     {
         bool hasKeyframe = targetLayer.KeyFrames.Any(x => x.IsInFrame(frame));
         bool hasKeyframe = targetLayer.KeyFrames.Any(x => x.IsInFrame(frame));

+ 32 - 7
src/PixiEditor.ChangeableDocument/Changes/NodeGraph/NodeOperations.cs

@@ -72,9 +72,11 @@ public static class NodeOperations
         return node;
         return node;
     }
     }
 
 
-    public static List<IChangeInfo> AppendMember(Node parent, Node toAppend, out Dictionary<Guid, VecD> originalPositions)
+    public static List<IChangeInfo> AppendMember(Node parent, Node toAppend,
+        out Dictionary<Guid, VecD> originalPositions)
     {
     {
-        InputProperty<Painter?>? parentInput = parent.GetInputProperty(OutputNode.InputPropertyName) as InputProperty<Painter?>;
+        InputProperty<Painter?>? parentInput =
+            parent.GetInputProperty(OutputNode.InputPropertyName) as InputProperty<Painter?>;
         if (parentInput == null)
         if (parentInput == null)
         {
         {
             throw new InvalidOperationException("Parent node does not have an input property for appending members.");
             throw new InvalidOperationException("Parent node does not have an input property for appending members.");
@@ -86,18 +88,21 @@ public static class NodeOperations
             throw new InvalidOperationException("Node to append does not have an output property named 'Output'.");
             throw new InvalidOperationException("Node to append does not have an output property named 'Output'.");
         }
         }
 
 
-        InputProperty<Painter>? toAddInput = toAppend.GetInputProperty(OutputNode.InputPropertyName) as InputProperty<Painter>;
+        InputProperty<Painter>? toAddInput =
+            toAppend.GetInputProperty(OutputNode.InputPropertyName) as InputProperty<Painter>;
 
 
         if (toAddInput == null)
         if (toAddInput == null)
         {
         {
-            throw new InvalidOperationException("Node to append does not have an input property for appending members.");
+            throw new InvalidOperationException(
+                "Node to append does not have an input property for appending members.");
         }
         }
 
 
         Guid memberId = toAppend.Id;
         Guid memberId = toAppend.Id;
 
 
         List<IChangeInfo> changes = AppendMember(parentInput, toAddOutput, toAddInput, memberId);
         List<IChangeInfo> changes = AppendMember(parentInput, toAddOutput, toAddInput, memberId);
 
 
-        var adjustedPositions = AdjustPositionsAfterAppend(toAppend, parent, parentInput.Connection?.Node as Node ?? null, out originalPositions);
+        var adjustedPositions = AdjustPositionsAfterAppend(toAppend, parent,
+            parentInput.Connection?.Node as Node ?? null, out originalPositions);
 
 
         changes.AddRange(adjustedPositions);
         changes.AddRange(adjustedPositions);
         return changes;
         return changes;
@@ -215,8 +220,8 @@ public static class NodeOperations
                 toMove.Position = pos;
                 toMove.Position = pos;
                 toMove.Position = new VecD(toMove.Position.X, y);
                 toMove.Position = new VecD(toMove.Position.X, y);
                 changes.Add(new NodePosition_ChangeInfo(toMove.Id, toMove.Position));
                 changes.Add(new NodePosition_ChangeInfo(toMove.Id, toMove.Position));
-                
-                if(aNode == appendedTo) return false;
+
+                if (aNode == appendedTo) return false;
             }
             }
 
 
             return true;
             return true;
@@ -270,15 +275,35 @@ public static class NodeOperations
     public static List<IChangeInfo> ConnectStructureNodeProperties(ConnectionsData originalConnections, Node node,
     public static List<IChangeInfo> ConnectStructureNodeProperties(ConnectionsData originalConnections, Node node,
         IReadOnlyNodeGraph graph)
         IReadOnlyNodeGraph graph)
     {
     {
+        if (node == null || originalConnections == null || graph == null)
+        {
+            return new List<IChangeInfo>();
+        }
+
         List<IChangeInfo> changes = new();
         List<IChangeInfo> changes = new();
         foreach (var connections in originalConnections.originalOutputConnections)
         foreach (var connections in originalConnections.originalOutputConnections)
         {
         {
             PropertyConnection outputConnection = connections.Key;
             PropertyConnection outputConnection = connections.Key;
+            if (outputConnection == null)
+                continue;
+
             IOutputProperty outputProp = node.GetOutputProperty(outputConnection.PropertyName);
             IOutputProperty outputProp = node.GetOutputProperty(outputConnection.PropertyName);
+
+            if (outputProp == null)
+            {
+                continue;
+            }
+
             foreach (var connection in connections.Value)
             foreach (var connection in connections.Value)
             {
             {
                 var inputNode = graph.AllNodes.FirstOrDefault(x => x.Id == connection.NodeId);
                 var inputNode = graph.AllNodes.FirstOrDefault(x => x.Id == connection.NodeId);
+                if (inputNode is null)
+                    continue;
+
                 IInputProperty property = inputNode.GetInputProperty(connection.PropertyName);
                 IInputProperty property = inputNode.GetInputProperty(connection.PropertyName);
+                if (property is null)
+                    continue;
+
                 outputProp.ConnectTo(property);
                 outputProp.ConnectTo(property);
                 changes.Add(new ConnectProperty_ChangeInfo(node.Id, property.Node.Id, outputProp.InternalPropertyName,
                 changes.Add(new ConnectProperty_ChangeInfo(node.Id, property.Node.Id, outputProp.InternalPropertyName,
                     property.InternalPropertyName));
                     property.InternalPropertyName));

+ 21 - 1
src/PixiEditor.Desktop/Program.cs

@@ -1,4 +1,5 @@
 using System;
 using System;
+using System.Linq;
 using Avalonia;
 using Avalonia;
 using Avalonia.Logging;
 using Avalonia.Logging;
 using Drawie.Interop.Avalonia;
 using Drawie.Interop.Avalonia;
@@ -19,7 +20,26 @@ public class Program
     // Avalonia configuration, don't remove; also used by visual designer.
     // Avalonia configuration, don't remove; also used by visual designer.
     public static AppBuilder BuildAvaloniaApp()
     public static AppBuilder BuildAvaloniaApp()
     {
     {
-        bool openGlPreferred = string.Equals(RenderApiPreferenceManager.TryReadRenderApiPreference(), "opengl", StringComparison.OrdinalIgnoreCase);
+        bool openGlPreferred = false;
+        try
+        {
+            openGlPreferred = string.Equals(RenderApiPreferenceManager.TryReadRenderApiPreference(), "opengl",
+                StringComparison.OrdinalIgnoreCase);
+
+            if (!openGlPreferred)
+            {
+                var cmdArgs = Environment.GetCommandLineArgs();
+                if (cmdArgs is { Length: > 0 })
+                {
+                    openGlPreferred = cmdArgs.Any(arg =>
+                        string.Equals(arg, "--opengl", StringComparison.OrdinalIgnoreCase));
+                }
+            }
+        }
+        catch (Exception ex)
+        {
+        }
+
         return AppBuilder.Configure<App>()
         return AppBuilder.Configure<App>()
             .UsePlatformDetect()
             .UsePlatformDetect()
             .With(new Win32PlatformOptions()
             .With(new Win32PlatformOptions()

+ 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
 // You can specify all the values or you can default the Build and Revision Numbers
 // by using the '*' as shown below:
 // by using the '*' as shown below:
 // [assembly: AssemblyVersion("1.0.*")]
 // [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("2.0.1.5")]
-[assembly: AssemblyFileVersion("2.0.1.5")]
+[assembly: AssemblyVersion("2.0.1.6")]
+[assembly: AssemblyFileVersion("2.0.1.6")]

+ 6 - 0
src/PixiEditor/ViewModels/SubViewModels/LayersViewModel.cs

@@ -557,6 +557,12 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
             NoticeDialog.Show(title: "ERROR", message: e.Message);
             NoticeDialog.Show(title: "ERROR", message: e.Message);
             return;
             return;
         }
         }
+        catch (Exception e)
+        {
+            CrashHelper.SendExceptionInfo(e);
+            NoticeDialog.Show(title: "ERROR", message: e.Message);
+            return;
+        }
 
 
         byte[] bytes = bitmap.ToByteArray();
         byte[] bytes = bitmap.ToByteArray();
 
 

+ 1 - 1
src/PixiEditor/Views/Overlays/LineToolOverlay/LineToolOverlay.cs

@@ -174,7 +174,7 @@ internal class LineToolOverlay : Overlay
         isDraggingHandle = false;
         isDraggingHandle = false;
         IsSizeBoxEnabled = false;
         IsSizeBoxEnabled = false;
         
         
-        AddToUndoCommand.Execute((LineStart, LineEnd));
+        AddToUndoCommand?.Execute((LineStart, LineEnd));
     }
     }
 
 
     protected override void ZoomChanged(double newZoom)
     protected override void ZoomChanged(double newZoom)

+ 162 - 99
src/PixiEditor/Views/Rendering/Scene.cs

@@ -358,18 +358,25 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
         {
         {
             foreach (Overlay overlay in AllOverlays)
             foreach (Overlay overlay in AllOverlays)
             {
             {
-                if (!overlay.IsVisible || overlay.OverlayRenderSorting != sorting)
+                try
                 {
                 {
-                    continue;
-                }
+                    if (!overlay.IsVisible || overlay.OverlayRenderSorting != sorting)
+                    {
+                        continue;
+                    }
 
 
-                overlay.PointerPosition = lastMousePosition;
+                    overlay.PointerPosition = lastMousePosition;
 
 
-                overlay.ZoomScale = Scale;
+                    overlay.ZoomScale = Scale;
 
 
-                if (!overlay.CanRender()) continue;
+                    if (!overlay.CanRender()) continue;
 
 
-                overlay.RenderOverlay(renderSurface.Canvas, dirtyBounds);
+                    overlay.RenderOverlay(renderSurface.Canvas, dirtyBounds);
+                }
+                catch (Exception ex)
+                {
+                    CrashHelper.SendExceptionInfo(ex);
+                }
             }
             }
         }
         }
     }
     }
@@ -417,161 +424,203 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
     protected override void OnPointerMoved(PointerEventArgs e)
     protected override void OnPointerMoved(PointerEventArgs e)
     {
     {
         base.OnPointerMoved(e);
         base.OnPointerMoved(e);
-        if (AllOverlays != null)
+        try
         {
         {
-            OverlayPointerArgs args = ConstructPointerArgs(e);
-            lastMousePosition = args.Point;
+            if (AllOverlays != null)
+            {
+                OverlayPointerArgs args = ConstructPointerArgs(e);
+                lastMousePosition = args.Point;
 
 
-            Cursor finalCursor = DefaultCursor;
+                Cursor finalCursor = DefaultCursor;
 
 
-            if (capturedOverlay != null)
-            {
-                capturedOverlay.MovePointer(args);
-                if (capturedOverlay.IsHitTestVisible)
+                if (capturedOverlay != null)
                 {
                 {
-                    finalCursor = capturedOverlay.Cursor ?? DefaultCursor;
+                    capturedOverlay.MovePointer(args);
+                    if (capturedOverlay.IsHitTestVisible)
+                    {
+                        finalCursor = capturedOverlay.Cursor ?? DefaultCursor;
+                    }
                 }
                 }
-            }
-            else
-            {
-                foreach (Overlay overlay in AllOverlays)
+                else
                 {
                 {
-                    if (!overlay.IsVisible) continue;
-
-                    if (overlay.TestHit(args.Point))
+                    foreach (Overlay overlay in AllOverlays)
                     {
                     {
-                        if (!mouseOverOverlays.Contains(overlay))
+                        if (!overlay.IsVisible) continue;
+
+                        if (overlay.TestHit(args.Point))
                         {
                         {
-                            overlay.EnterPointer(args);
-                            mouseOverOverlays.Add(overlay);
+                            if (!mouseOverOverlays.Contains(overlay))
+                            {
+                                overlay.EnterPointer(args);
+                                mouseOverOverlays.Add(overlay);
+                            }
                         }
                         }
-                    }
-                    else
-                    {
-                        if (mouseOverOverlays.Contains(overlay))
+                        else
                         {
                         {
-                            overlay.ExitPointer(args);
-                            mouseOverOverlays.Remove(overlay);
-
-                            e.Handled = args.Handled;
-                            return;
+                            if (mouseOverOverlays.Contains(overlay))
+                            {
+                                overlay.ExitPointer(args);
+                                mouseOverOverlays.Remove(overlay);
+
+                                e.Handled = args.Handled;
+                                return;
+                            }
                         }
                         }
-                    }
 
 
-                    overlay.MovePointer(args);
-                    if (overlay.IsHitTestVisible)
-                    {
-                        finalCursor = overlay.Cursor ?? DefaultCursor;
+                        overlay.MovePointer(args);
+                        if (overlay.IsHitTestVisible)
+                        {
+                            finalCursor = overlay.Cursor ?? DefaultCursor;
+                        }
                     }
                     }
                 }
                 }
-            }
 
 
-            if (Cursor.ToString() != finalCursor.ToString())
-                Cursor = finalCursor;
-            e.Handled = args.Handled;
+                if (Cursor.ToString() != finalCursor.ToString())
+                    Cursor = finalCursor;
+                e.Handled = args.Handled;
+            }
+        }
+        catch (Exception ex)
+        {
+            CrashHelper.SendExceptionInfo(ex);
         }
         }
     }
     }
 
 
     protected override void OnPointerPressed(PointerPressedEventArgs e)
     protected override void OnPointerPressed(PointerPressedEventArgs e)
     {
     {
         base.OnPointerPressed(e);
         base.OnPointerPressed(e);
-        if (AllOverlays != null)
+        try
         {
         {
-            OverlayPointerArgs args = ConstructPointerArgs(e);
-            if (capturedOverlay != null)
-            {
-                capturedOverlay?.PressPointer(args);
-            }
-            else
+            if (AllOverlays != null)
             {
             {
-                foreach (var overlay in AllOverlays)
+                OverlayPointerArgs args = ConstructPointerArgs(e);
+                if (capturedOverlay != null)
                 {
                 {
-                    if (args.Handled) break;
-                    if (!overlay.IsVisible) continue;
+                    capturedOverlay?.PressPointer(args);
+                }
+                else
+                {
+                    foreach (var overlay in AllOverlays)
+                    {
+                        if (args.Handled) break;
+                        if (!overlay.IsVisible) continue;
 
 
-                    if (!overlay.IsHitTestVisible || !overlay.TestHit(args.Point)) continue;
+                        if (!overlay.IsHitTestVisible || !overlay.TestHit(args.Point)) continue;
 
 
-                    overlay.PressPointer(args);
+                        overlay.PressPointer(args);
+                    }
                 }
                 }
-            }
 
 
-            e.Handled = args.Handled;
+                e.Handled = args.Handled;
+            }
+        }
+        catch (Exception ex)
+        {
+            CrashHelper.SendExceptionInfo(ex);
         }
         }
     }
     }
 
 
     protected override void OnPointerExited(PointerEventArgs e)
     protected override void OnPointerExited(PointerEventArgs e)
     {
     {
         base.OnPointerExited(e);
         base.OnPointerExited(e);
-        if (AllOverlays != null)
+        try
         {
         {
-            OverlayPointerArgs args = ConstructPointerArgs(e);
-            for (var i = 0; i < mouseOverOverlays.Count; i++)
+            if (AllOverlays != null)
             {
             {
-                var overlay = mouseOverOverlays[i];
-                if (args.Handled) break;
-                if (!overlay.IsVisible) continue;
+                OverlayPointerArgs args = ConstructPointerArgs(e);
+                for (var i = 0; i < mouseOverOverlays.Count; i++)
+                {
+                    var overlay = mouseOverOverlays[i];
+                    if (args.Handled) break;
+                    if (!overlay.IsVisible) continue;
 
 
-                overlay.ExitPointer(args);
-                mouseOverOverlays.Remove(overlay);
-                i--;
-            }
+                    overlay.ExitPointer(args);
+                    mouseOverOverlays.Remove(overlay);
+                    i--;
+                }
 
 
-            e.Handled = args.Handled;
+                e.Handled = args.Handled;
+            }
+        }
+        catch (Exception ex)
+        {
+            CrashHelper.SendExceptionInfo(ex);
         }
         }
     }
     }
 
 
     protected override void OnPointerReleased(PointerReleasedEventArgs e)
     protected override void OnPointerReleased(PointerReleasedEventArgs e)
     {
     {
         base.OnPointerExited(e);
         base.OnPointerExited(e);
-        if (AllOverlays != null)
+        try
         {
         {
-            OverlayPointerArgs args = ConstructPointerArgs(e);
-
-            if (capturedOverlay != null)
-            {
-                capturedOverlay.ReleasePointer(args);
-                capturedOverlay = null;
-            }
-            else
+            if (AllOverlays != null)
             {
             {
-                foreach (Overlay overlay in AllOverlays)
+                OverlayPointerArgs args = ConstructPointerArgs(e);
+
+                if (capturedOverlay != null)
                 {
                 {
-                    if (args.Handled) break;
-                    if (!overlay.IsVisible) continue;
+                    capturedOverlay.ReleasePointer(args);
+                    capturedOverlay = null;
+                }
+                else
+                {
+                    foreach (Overlay overlay in AllOverlays)
+                    {
+                        if (args.Handled) break;
+                        if (!overlay.IsVisible) continue;
 
 
-                    if (!overlay.IsHitTestVisible || !overlay.TestHit(args.Point)) continue;
+                        if (!overlay.IsHitTestVisible || !overlay.TestHit(args.Point)) continue;
 
 
-                    overlay.ReleasePointer(args);
+                        overlay.ReleasePointer(args);
+                    }
                 }
                 }
             }
             }
         }
         }
+        catch (Exception ex)
+        {
+            CrashHelper.SendExceptionInfo(ex);
+        }
     }
     }
 
 
     protected override void OnKeyDown(KeyEventArgs e)
     protected override void OnKeyDown(KeyEventArgs e)
     {
     {
         base.OnKeyDown(e);
         base.OnKeyDown(e);
-        if (AllOverlays != null)
+        try
         {
         {
-            foreach (Overlay overlay in AllOverlays)
+            if (AllOverlays != null)
             {
             {
-                if (!overlay.IsVisible) continue;
+                foreach (Overlay overlay in AllOverlays)
+                {
+                    if (!overlay.IsVisible) continue;
 
 
-                overlay.KeyPressed(e);
+                    overlay.KeyPressed(e);
+                }
             }
             }
         }
         }
+        catch (Exception ex)
+        {
+            CrashHelper.SendExceptionInfo(ex);
+        }
     }
     }
 
 
     protected override void OnKeyUp(KeyEventArgs e)
     protected override void OnKeyUp(KeyEventArgs e)
     {
     {
         base.OnKeyUp(e);
         base.OnKeyUp(e);
-        if (AllOverlays != null)
+        try
         {
         {
-            foreach (Overlay overlay in AllOverlays)
+            if (AllOverlays != null)
             {
             {
-                if (!overlay.IsVisible) continue;
-                overlay.KeyReleased(e);
+                foreach (Overlay overlay in AllOverlays)
+                {
+                    if (!overlay.IsVisible) continue;
+                    overlay.KeyReleased(e);
+                }
             }
             }
         }
         }
+        catch (Exception ex)
+        {
+            CrashHelper.SendExceptionInfo(ex);
+        }
     }
     }
 
 
     private OverlayPointerArgs ConstructPointerArgs(PointerEventArgs e)
     private OverlayPointerArgs ConstructPointerArgs(PointerEventArgs e)
@@ -597,27 +646,41 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
     protected override void OnGotFocus(GotFocusEventArgs e)
     protected override void OnGotFocus(GotFocusEventArgs e)
     {
     {
         base.OnGotFocus(e);
         base.OnGotFocus(e);
-        if (AllOverlays != null)
+        try
         {
         {
-            foreach (Overlay overlay in AllOverlays)
+            if (AllOverlays != null)
             {
             {
-                if (!overlay.IsVisible) continue;
-                overlay.FocusChanged(true);
+                foreach (Overlay overlay in AllOverlays)
+                {
+                    if (!overlay.IsVisible) continue;
+                    overlay.FocusChanged(true);
+                }
             }
             }
         }
         }
+        catch (Exception ex)
+        {
+            CrashHelper.SendExceptionInfo(ex);
+        }
     }
     }
 
 
     protected override void OnLostFocus(RoutedEventArgs e)
     protected override void OnLostFocus(RoutedEventArgs e)
     {
     {
         base.OnLostFocus(e);
         base.OnLostFocus(e);
-        if (AllOverlays != null)
+        try
         {
         {
-            foreach (Overlay overlay in AllOverlays)
+            if (AllOverlays != null)
             {
             {
-                if (!overlay.IsVisible) continue;
-                overlay.FocusChanged(false);
+                foreach (Overlay overlay in AllOverlays)
+                {
+                    if (!overlay.IsVisible) continue;
+                    overlay.FocusChanged(false);
+                }
             }
             }
         }
         }
+        catch (Exception ex)
+        {
+            CrashHelper.SendExceptionInfo(ex);
+        }
     }
     }
 
 
     private VecD ToCanvasSpace(Point scenePosition)
     private VecD ToCanvasSpace(Point scenePosition)