Browse Source

Make Folder Children immutable for thread safety; minor refactor

Equbuxu 3 years ago
parent
commit
644f0748a1
19 changed files with 73 additions and 78 deletions
  1. 3 2
      src/PixiEditor.ChangeableDocument/Changeables/Document.cs
  2. 11 9
      src/PixiEditor.ChangeableDocument/Changeables/Folder.cs
  3. 3 5
      src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyDocument.cs
  4. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyFolder.cs
  5. 2 4
      src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyLayer.cs
  6. 3 3
      src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlySelection.cs
  7. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyStructureMember.cs
  8. 2 4
      src/PixiEditor.ChangeableDocument/Changeables/Layer.cs
  9. 4 5
      src/PixiEditor.ChangeableDocument/Changeables/Selection.cs
  10. 2 3
      src/PixiEditor.ChangeableDocument/Changeables/StructureMember.cs
  11. 2 2
      src/PixiEditor.ChangeableDocument/Changes/Structure/CreateStructureMember_Change.cs
  12. 2 2
      src/PixiEditor.ChangeableDocument/Changes/Structure/DeleteStructureMember_Change.cs
  13. 2 2
      src/PixiEditor.ChangeableDocument/Changes/Structure/MoveStructureMember_Change.cs
  14. 16 16
      src/PixiEditor.ChangeableDocument/Rendering/ChunkRenderer.cs
  15. 3 3
      src/PixiEditorPrototype/Models/DocumentUpdater.cs
  16. 9 9
      src/PixiEditorPrototype/Models/Rendering/AffectedChunkGatherer.cs
  17. 3 3
      src/PixiEditorPrototype/Models/Rendering/WriteableBitmapUpdater.cs
  18. 2 2
      src/PixiEditorPrototype/ViewModels/DocumentViewModel.cs
  19. 2 2
      src/PixiEditorPrototype/ViewModels/StructureMemberViewModel.cs

+ 3 - 2
src/PixiEditor.ChangeableDocument/Changeables/Document.cs

@@ -4,8 +4,8 @@ namespace PixiEditor.ChangeableDocument.Changeables;
 
 internal class Document : IChangeable, IReadOnlyDocument, IDisposable
 {
-    public IReadOnlyFolder ReadOnlyStructureRoot => StructureRoot;
-    public IReadOnlySelection ReadOnlySelection => Selection;
+    IReadOnlyFolder IReadOnlyDocument.StructureRoot => StructureRoot;
+    IReadOnlySelection IReadOnlyDocument.Selection => Selection;
     IReadOnlyStructureMember? IReadOnlyDocument.FindMember(Guid guid) => FindMember(guid);
     IReadOnlyList<IReadOnlyStructureMember> IReadOnlyDocument.FindMemberPath(Guid guid) => FindMemberPath(guid);
     IReadOnlyStructureMember IReadOnlyDocument.FindMemberOrThrow(Guid guid) => FindMemberOrThrow(guid);
@@ -66,6 +66,7 @@ internal class Document : IChangeable, IReadOnlyDocument, IDisposable
         {
             return true;
         }
+
         foreach (var member in folder.Children)
         {
             if (member is Layer childLayer && childLayer.GuidValue == guid)

+ 11 - 9
src/PixiEditor.ChangeableDocument/Changeables/Folder.cs

@@ -1,18 +1,20 @@
-using PixiEditor.ChangeableDocument.Changeables.Interfaces;
+using System.Collections.Immutable;
+using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 
 namespace PixiEditor.ChangeableDocument.Changeables;
 
 internal class Folder : StructureMember, IReadOnlyFolder
 {
-    internal List<StructureMember> Children { get; set; } = new();
-    public IReadOnlyList<IReadOnlyStructureMember> ReadOnlyChildren => Children;
+    public ImmutableList<StructureMember> Children { get; set; } = ImmutableList<StructureMember>.Empty;
+    IReadOnlyList<IReadOnlyStructureMember> IReadOnlyFolder.Children => Children;
 
     internal override Folder Clone()
     {
-        List<StructureMember> clonedChildren = new();
-        foreach (var child in Children)
+        var builder = ImmutableList<StructureMember>.Empty.ToBuilder();
+        for (var i = 0; i < Children.Count; i++)
         {
-            clonedChildren.Add(child.Clone());
+            var child = Children[i];
+            builder.Add(child.Clone());
         }
 
         return new Folder()
@@ -21,16 +23,16 @@ internal class Folder : StructureMember, IReadOnlyFolder
             IsVisible = IsVisible,
             Name = Name,
             Opacity = Opacity,
-            Children = clonedChildren,
+            Children = builder.ToImmutable(),
             Mask = Mask?.CloneFromCommitted()
         };
     }
 
     public override void Dispose()
     {
-        foreach (var child in Children)
+        for (var i = 0; i < Children.Count; i++)
         {
-            child.Dispose();
+            Children[i].Dispose();
         }
         Mask?.Dispose();
     }

+ 3 - 5
src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyDocument.cs

@@ -1,11 +1,9 @@
-using ChunkyImageLib.DataHolders;
-
-namespace PixiEditor.ChangeableDocument.Changeables.Interfaces;
+namespace PixiEditor.ChangeableDocument.Changeables.Interfaces;
 
 public interface IReadOnlyDocument
 {
-    IReadOnlyFolder ReadOnlyStructureRoot { get; }
-    IReadOnlySelection ReadOnlySelection { get; }
+    IReadOnlyFolder StructureRoot { get; }
+    IReadOnlySelection Selection { get; }
     VecI Size { get; }
     bool HorizontalSymmetryAxisEnabled { get; }
     bool VerticalSymmetryAxisEnabled { get; }

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyFolder.cs

@@ -2,5 +2,5 @@
 
 public interface IReadOnlyFolder : IReadOnlyStructureMember
 {
-    IReadOnlyList<IReadOnlyStructureMember> ReadOnlyChildren { get; }
+    IReadOnlyList<IReadOnlyStructureMember> Children { get; }
 }

+ 2 - 4
src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyLayer.cs

@@ -1,9 +1,7 @@
-using ChunkyImageLib;
-
-namespace PixiEditor.ChangeableDocument.Changeables.Interfaces;
+namespace PixiEditor.ChangeableDocument.Changeables.Interfaces;
 
 public interface IReadOnlyLayer : IReadOnlyStructureMember
 {
-    IReadOnlyChunkyImage ReadOnlyLayerImage { get; }
+    IReadOnlyChunkyImage LayerImage { get; }
     bool LockTransparency { get; }
 }

+ 3 - 3
src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlySelection.cs

@@ -5,7 +5,7 @@ namespace PixiEditor.ChangeableDocument.Changeables.Interfaces;
 
 public interface IReadOnlySelection
 {
-    public IReadOnlyChunkyImage ReadOnlySelectionImage { get; }
-    public bool ReadOnlyIsEmptyAndInactive { get; }
-    public SKPath ReadOnlySelectionPath { get; }
+    public IReadOnlyChunkyImage SelectionImage { get; }
+    public bool IsEmptyAndInactive { get; }
+    public SKPath SelectionPath { get; }
 }

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyStructureMember.cs

@@ -11,5 +11,5 @@ public interface IReadOnlyStructureMember
     Guid GuidValue { get; }
     float Opacity { get; }
     BlendMode BlendMode { get; }
-    IReadOnlyChunkyImage? ReadOnlyMask { get; }
+    IReadOnlyChunkyImage? Mask { get; }
 }

+ 2 - 4
src/PixiEditor.ChangeableDocument/Changeables/Layer.cs

@@ -1,6 +1,4 @@
-using ChunkyImageLib;
-using ChunkyImageLib.DataHolders;
-using PixiEditor.ChangeableDocument.Changeables.Interfaces;
+using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 
 namespace PixiEditor.ChangeableDocument.Changeables;
 
@@ -8,7 +6,7 @@ internal class Layer : StructureMember, IReadOnlyLayer
 {
     public bool LockTransparency { get; set; } = false;
     public ChunkyImage LayerImage { get; set; }
-    IReadOnlyChunkyImage IReadOnlyLayer.ReadOnlyLayerImage => LayerImage;
+    IReadOnlyChunkyImage IReadOnlyLayer.LayerImage => LayerImage;
 
     public Layer(VecI size)
     {

+ 4 - 5
src/PixiEditor.ChangeableDocument/Changeables/Selection.cs

@@ -1,5 +1,4 @@
-using ChunkyImageLib;
-using PixiEditor.ChangeableDocument.Changeables.Interfaces;
+using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using SkiaSharp;
 
 namespace PixiEditor.ChangeableDocument.Changeables;
@@ -10,10 +9,10 @@ internal class Selection : IReadOnlySelection, IDisposable
     public bool IsEmptyAndInactive { get; set; } = true;
     public ChunkyImage SelectionImage { get; set; } = new(new(64, 64));
     public SKPath SelectionPath { get; set; } = new();
-    public SKPath ReadOnlySelectionPath => new SKPath(SelectionPath);
+    SKPath IReadOnlySelection.SelectionPath => new SKPath(SelectionPath);
 
-    public IReadOnlyChunkyImage ReadOnlySelectionImage => SelectionImage;
-    public bool ReadOnlyIsEmptyAndInactive => IsEmptyAndInactive;
+    IReadOnlyChunkyImage IReadOnlySelection.SelectionImage => SelectionImage;
+    bool IReadOnlySelection.IsEmptyAndInactive => IsEmptyAndInactive;
 
     public void Dispose()
     {

+ 2 - 3
src/PixiEditor.ChangeableDocument/Changeables/StructureMember.cs

@@ -1,5 +1,4 @@
-using ChunkyImageLib;
-using PixiEditor.ChangeableDocument.Changeables.Interfaces;
+using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.ChangeableDocument.Enums;
 
 namespace PixiEditor.ChangeableDocument.Changeables;
@@ -13,7 +12,7 @@ internal abstract class StructureMember : IChangeable, IReadOnlyStructureMember,
     public BlendMode BlendMode { get; set; } = BlendMode.Normal;
     public Guid GuidValue { get; init; }
     public ChunkyImage? Mask { get; set; } = null;
-    public IReadOnlyChunkyImage? ReadOnlyMask => Mask;
+    IReadOnlyChunkyImage? IReadOnlyStructureMember.Mask => Mask;
 
     internal abstract StructureMember Clone();
     public abstract void Dispose();

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changes/Structure/CreateStructureMember_Change.cs

@@ -38,7 +38,7 @@ internal class CreateStructureMember_Change : Change
             _ => throw new InvalidOperationException("Cannon create member of type " + type.ToString())
         };
 
-        folder.Children.Insert(parentFolderIndex, member);
+        folder.Children = folder.Children.Insert(parentFolderIndex, member);
 
         ignoreInUndo = false;
         return new CreateStructureMember_ChangeInfo() { GuidValue = newMemberGuid };
@@ -49,7 +49,7 @@ internal class CreateStructureMember_Change : Change
         var folder = (Folder)document.FindMemberOrThrow(parentFolderGuid);
         var child = document.FindMemberOrThrow(newMemberGuid);
         child.Dispose();
-        folder.Children.RemoveAt(folder.Children.FindIndex(child => child.GuidValue == newMemberGuid));
+        folder.Children = folder.Children.RemoveAt(folder.Children.FindIndex(child => child.GuidValue == newMemberGuid));
 
         return new DeleteStructureMember_ChangeInfo() { GuidValue = newMemberGuid, ParentGuid = parentFolderGuid };
     }

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changes/Structure/DeleteStructureMember_Change.cs

@@ -30,7 +30,7 @@ internal class DeleteStructureMember_Change : Change
     public override IChangeInfo Apply(Document document, out bool ignoreInUndo)
     {
         var (member, parent) = document.FindChildAndParentOrThrow(memberGuid);
-        parent.Children.Remove(member);
+        parent.Children = parent.Children.Remove(member);
         member.Dispose();
         ignoreInUndo = false;
         return new DeleteStructureMember_ChangeInfo() { GuidValue = memberGuid, ParentGuid = parentGuid };
@@ -40,7 +40,7 @@ internal class DeleteStructureMember_Change : Change
     {
         var parent = (Folder)doc.FindMemberOrThrow(parentGuid);
 
-        parent.Children.Insert(originalIndex, savedCopy!.Clone());
+        parent.Children = parent.Children.Insert(originalIndex, savedCopy!.Clone());
         return new CreateStructureMember_ChangeInfo() { GuidValue = memberGuid };
     }
 

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changes/Structure/MoveStructureMember_Change.cs

@@ -36,8 +36,8 @@ internal class MoveStructureMember_Change : Change
         var targetFolder = (Folder)document.FindMemberOrThrow(targetFolderGuid);
         var (member, curFolder) = document.FindChildAndParentOrThrow(memberGuid);
 
-        curFolder.Children.Remove(member);
-        targetFolder.Children.Insert(targetIndex, member);
+        curFolder.Children = curFolder.Children.Remove(member);
+        targetFolder.Children = targetFolder.Children.Insert(targetIndex, member);
     }
 
     public override IChangeInfo? Apply(Document target, out bool ignoreInUndo)

+ 16 - 16
src/PixiEditor.ChangeableDocument/Rendering/ChunkRenderer.cs

@@ -32,20 +32,20 @@ public static class ChunkRenderer
             clippingChunk.IsT1 ||
             !layer.IsVisible ||
             layer.Opacity == 0 ||
-            (layer.ReadOnlyMask is not null && !layer.ReadOnlyMask.LatestOrCommittedChunkExists(chunkPos))
+            (layer.Mask is not null && !layer.Mask.LatestOrCommittedChunkExists(chunkPos))
             )
             return new EmptyChunk();
 
         context.UpdateFromMember(layer);
 
         Chunk renderingResult = Chunk.Create(resolution);
-        if (!layer.ReadOnlyLayerImage.DrawMostUpToDateChunkOn(chunkPos, resolution, renderingResult.Surface.SkiaSurface, new(0, 0), context.ReplacingPaintWithOpacity))
+        if (!layer.LayerImage.DrawMostUpToDateChunkOn(chunkPos, resolution, renderingResult.Surface.SkiaSurface, new(0, 0), context.ReplacingPaintWithOpacity))
         {
             renderingResult.Dispose();
             return new EmptyChunk();
         }
 
-        if (!layer.ReadOnlyMask!.DrawMostUpToDateChunkOn(chunkPos, resolution, renderingResult.Surface.SkiaSurface, new(0, 0), ClippingPaint))
+        if (!layer.Mask!.DrawMostUpToDateChunkOn(chunkPos, resolution, renderingResult.Surface.SkiaSurface, new(0, 0), ClippingPaint))
         {
             // should pretty much never happen due to the check above, but you can never be sure with many threads
             renderingResult.Dispose();
@@ -65,12 +65,12 @@ public static class ChunkRenderer
         if (clippingChunk.IsT1 || !layer.IsVisible || layer.Opacity == 0)
             return new EmptyChunk();
 
-        if (layer.ReadOnlyMask is not null)
+        if (layer.Mask is not null)
             return RenderLayerWithMask(context, targetChunk, chunkPos, resolution, layer, clippingChunk);
 
         context.UpdateFromMember(layer);
         Chunk renderingResult = Chunk.Create(resolution);
-        if (!layer.ReadOnlyLayerImage.DrawMostUpToDateChunkOn(chunkPos, resolution, renderingResult.Surface.SkiaSurface, new(0, 0), context.ReplacingPaintWithOpacity))
+        if (!layer.LayerImage.DrawMostUpToDateChunkOn(chunkPos, resolution, renderingResult.Surface.SkiaSurface, new(0, 0), context.ReplacingPaintWithOpacity))
         {
             renderingResult.Dispose();
             return new EmptyChunk();
@@ -87,7 +87,7 @@ public static class ChunkRenderer
     {
         if (clippingChunk.IsT1 || !layer.IsVisible || layer.Opacity == 0)
             return;
-        if (layer.ReadOnlyMask is not null)
+        if (layer.Mask is not null)
         {
             var result = RenderLayerWithMask(context, targetChunk, chunkPos, resolution, layer, clippingChunk);
             if (result.IsT1)
@@ -103,7 +103,7 @@ public static class ChunkRenderer
             return;
         }
         context.UpdateFromMember(layer);
-        layer.ReadOnlyLayerImage.DrawMostUpToDateChunkOn(chunkPos, resolution, targetChunk.Surface.SkiaSurface, new(0, 0), context.BlendModeOpacityPaint);
+        layer.LayerImage.DrawMostUpToDateChunkOn(chunkPos, resolution, targetChunk.Surface.SkiaSurface, new(0, 0), context.BlendModeOpacityPaint);
     }
 
     private static OneOf<EmptyChunk, Chunk> RenderFolder(
@@ -119,8 +119,8 @@ public static class ChunkRenderer
             clippingChunk.IsT1 ||
             !folder.IsVisible ||
             folder.Opacity == 0 ||
-            folder.ReadOnlyChildren.Count == 0 ||
-            (folder.ReadOnlyMask is not null && !folder.ReadOnlyMask.LatestOrCommittedChunkExists(chunkPos))
+            folder.Children.Count == 0 ||
+            (folder.Mask is not null && !folder.Mask.LatestOrCommittedChunkExists(chunkPos))
             )
             return new EmptyChunk();
 
@@ -129,9 +129,9 @@ public static class ChunkRenderer
             return new EmptyChunk();
         Chunk contents = maybeContents.AsT0;
 
-        if (folder.ReadOnlyMask is not null)
+        if (folder.Mask is not null)
         {
-            if (!folder.ReadOnlyMask.DrawMostUpToDateChunkOn(chunkPos, resolution, contents.Surface.SkiaSurface, new(0, 0), ClippingPaint))
+            if (!folder.Mask.DrawMostUpToDateChunkOn(chunkPos, resolution, contents.Surface.SkiaSurface, new(0, 0), ClippingPaint))
             {
                 // this shouldn't really happen due to the check above, but another thread could edit the mask in the meantime
                 contents.Dispose();
@@ -155,7 +155,7 @@ public static class ChunkRenderer
         IReadOnlyFolder folder,
         OneOf<All, HashSet<Guid>> membersToMerge)
     {
-        if (folder.ReadOnlyChildren.Count == 0)
+        if (folder.Children.Count == 0)
             return new EmptyChunk();
 
         // clipping to member below doesn't make sense if we are skipping some of them
@@ -165,16 +165,16 @@ public static class ChunkRenderer
         targetChunk.Surface.SkiaSurface.Canvas.Clear();
 
         OneOf<FilledChunk, EmptyChunk, Chunk> clippingChunk = new FilledChunk();
-        for (int i = 0; i < folder.ReadOnlyChildren.Count; i++)
+        for (int i = 0; i < folder.Children.Count; i++)
         {
-            var child = folder.ReadOnlyChildren[i];
+            var child = folder.Children[i];
 
             // next child might use clip to member below in which case we need to save the clip image
             bool needToSaveClippingChunk =
                 !ignoreClipToBelow &&
-                i < folder.ReadOnlyChildren.Count - 1 &&
+                i < folder.Children.Count - 1 &&
                 !child.ClipToMemberBelow &&
-                folder.ReadOnlyChildren[i + 1].ClipToMemberBelow;
+                folder.Children[i + 1].ClipToMemberBelow;
 
             // if the current member doesn't need a clip, get rid of it
             if (!child.ClipToMemberBelow && !clippingChunk.IsT0)

+ 3 - 3
src/PixiEditorPrototype/Models/DocumentUpdater.cs

@@ -214,7 +214,7 @@ internal class DocumentUpdater
         var (member, parentFolder) = helper.Tracker.Document.FindChildAndParentOrThrow(info.GuidValue);
         var parentFolderVM = (FolderViewModel)helper.StructureHelper.FindOrThrow(parentFolder.GuidValue);
 
-        int index = parentFolder.ReadOnlyChildren.IndexOf(member);
+        int index = parentFolder.Children.IndexOf(member);
 
         StructureMemberViewModel memberVM = member switch
         {
@@ -227,7 +227,7 @@ internal class DocumentUpdater
 
         if (member is IReadOnlyFolder folder2)
         {
-            foreach (IReadOnlyStructureMember child in folder2.ReadOnlyChildren)
+            foreach (IReadOnlyStructureMember child in folder2.Children)
             {
                 ProcessCreateStructureMember(new CreateStructureMember_ChangeInfo() { GuidValue = child.GuidValue });
             }
@@ -263,7 +263,7 @@ internal class DocumentUpdater
         var (memberVM, curFolderVM) = helper.StructureHelper.FindChildAndParentOrThrow(info.GuidValue);
         var (member, targetFolder) = helper.Tracker.Document.FindChildAndParentOrThrow(info.GuidValue);
 
-        int index = targetFolder.ReadOnlyChildren.IndexOf(member);
+        int index = targetFolder.Children.IndexOf(member);
         var targetFolderVM = (FolderViewModel)helper.StructureHelper.FindOrThrow(targetFolder.GuidValue);
 
         curFolderVM.Children.Remove(memberVM);

+ 9 - 9
src/PixiEditorPrototype/Models/Rendering/AffectedChunkGatherer.cs

@@ -94,7 +94,7 @@ internal class AffectedChunkGatherer
         var member = tracker.Document.FindMember(memberGuid);
         if (member is IReadOnlyLayer layer)
         {
-            var chunks = layer.ReadOnlyLayerImage.FindAllChunks();
+            var chunks = layer.LayerImage.FindAllChunks();
             AddToImagePreviews(memberGuid, chunks, ignoreSelf);
         }
         else
@@ -107,9 +107,9 @@ internal class AffectedChunkGatherer
         var member = tracker.Document.FindMember(memberGuid);
         if (member is IReadOnlyLayer layer)
         {
-            var chunks = layer.ReadOnlyLayerImage.FindAllChunks();
-            if (layer.ReadOnlyMask is not null)
-                chunks.IntersectWith(layer.ReadOnlyMask.FindAllChunks());
+            var chunks = layer.LayerImage.FindAllChunks();
+            if (layer.Mask is not null)
+                chunks.IntersectWith(layer.Mask.FindAllChunks());
             AddToMainImage(chunks);
         }
         else
@@ -120,9 +120,9 @@ internal class AffectedChunkGatherer
     private void AddAllToMaskPreview(Guid memberGuid)
     {
         var member = tracker.Document.FindMember(memberGuid);
-        if (member is null || member.ReadOnlyMask is null)
+        if (member is null || member.Mask is null)
             return;
-        var chunks = member.ReadOnlyMask.FindAllChunks();
+        var chunks = member.Mask.FindAllChunks();
         AddToMaskPreview(memberGuid, chunks);
     }
 
@@ -182,18 +182,18 @@ internal class AffectedChunkGatherer
 
     private void AddWholeCanvasToEveryImagePreview()
     {
-        ForEveryMember(tracker.Document.ReadOnlyStructureRoot, (member) => AddWholeCanvasToImagePreviews(member.GuidValue));
+        ForEveryMember(tracker.Document.StructureRoot, (member) => AddWholeCanvasToImagePreviews(member.GuidValue));
     }
 
     private void AddWholeCanvasToEveryMaskPreview()
     {
-        ForEveryMember(tracker.Document.ReadOnlyStructureRoot, (member) => AddWholeCanvasToMaskPreview(member.GuidValue));
+        ForEveryMember(tracker.Document.StructureRoot, (member) => AddWholeCanvasToMaskPreview(member.GuidValue));
     }
 
 
     private void ForEveryMember(IReadOnlyFolder folder, Action<IReadOnlyStructureMember> action)
     {
-        foreach (var child in folder.ReadOnlyChildren)
+        foreach (var child in folder.Children)
         {
             action(child);
             if (child is IReadOnlyFolder innerFolder)

+ 3 - 3
src/PixiEditorPrototype/Models/Rendering/WriteableBitmapUpdater.cs

@@ -136,7 +136,7 @@ internal class WriteableBitmapUpdater
                 {
                     var pos = chunk * ChunkResolution.Full.PixelSize();
                     // the full res chunks are already rendered so drawing them again should be fast
-                    layer.ReadOnlyLayerImage.DrawMostUpToDateChunkOn
+                    layer.LayerImage.DrawMostUpToDateChunkOn
                         (chunk, ChunkResolution.Full, memberVM.PreviewSurface, pos, ReplacingPaint);
                 }
                 infos.Add(new PreviewDirty_RenderInfo(guid));
@@ -181,7 +181,7 @@ internal class WriteableBitmapUpdater
             foreach (var chunk in chunks)
             {
                 var pos = chunk * ChunkResolution.Full.PixelSize();
-                member.ReadOnlyMask!.DrawMostUpToDateChunkOn
+                member.Mask!.DrawMostUpToDateChunkOn
                     (chunk, ChunkResolution.Full, memberVM.MaskPreviewSurface, pos, ReplacingPaint);
             }
 
@@ -210,7 +210,7 @@ internal class WriteableBitmapUpdater
 
     private void RenderChunk(VecI chunkPos, SKSurface screenSurface, ChunkResolution resolution)
     {
-        ChunkRenderer.MergeWholeStructure(chunkPos, resolution, helpers.Tracker.Document.ReadOnlyStructureRoot).Switch(
+        ChunkRenderer.MergeWholeStructure(chunkPos, resolution, helpers.Tracker.Document.StructureRoot).Switch(
             (Chunk chunk) =>
             {
                 screenSurface.Canvas.DrawSurface(chunk.Surface.SkiaSurface, chunkPos.Multiply(chunk.PixelSize), ReplacingPaint);

+ 2 - 2
src/PixiEditorPrototype/ViewModels/DocumentViewModel.cs

@@ -69,7 +69,7 @@ internal class DocumentViewModel : INotifyPropertyChanged
 
     public int Width => Helpers.Tracker.Document.Size.X;
     public int Height => Helpers.Tracker.Document.Size.Y;
-    public SKPath SelectionPath => Helpers.Tracker.Document.ReadOnlySelection.ReadOnlySelectionPath;
+    public SKPath SelectionPath => Helpers.Tracker.Document.Selection.SelectionPath;
     public Guid GuidValue { get; } = Guid.NewGuid();
     public int HorizontalSymmetryAxisY => Helpers.Tracker.Document.HorizontalSymmetryAxisY;
     public int VerticalSymmetryAxisX => Helpers.Tracker.Document.VerticalSymmetryAxisX;
@@ -99,7 +99,7 @@ internal class DocumentViewModel : INotifyPropertyChanged
         TransformViewModel.TransformMoved += OnTransformUpdate;
 
         Helpers = new DocumentHelpers(this);
-        StructureRoot = new FolderViewModel(this, Helpers, Helpers.Tracker.Document.ReadOnlyStructureRoot);
+        StructureRoot = new FolderViewModel(this, Helpers, Helpers.Tracker.Document.StructureRoot);
 
         UndoCommand = new RelayCommand(Undo);
         RedoCommand = new RelayCommand(Redo);

+ 2 - 2
src/PixiEditorPrototype/ViewModels/StructureMemberViewModel.cs

@@ -49,7 +49,7 @@ internal abstract class StructureMemberViewModel : INotifyPropertyChanged
 
     public Guid GuidValue => member.GuidValue;
 
-    public bool HasMask => member.ReadOnlyMask is not null;
+    public bool HasMask => member.Mask is not null;
 
     public const int PreviewSize = 48;
     public WriteableBitmap PreviewBitmap { get; set; }
@@ -91,7 +91,7 @@ internal abstract class StructureMemberViewModel : INotifyPropertyChanged
         var previewSize = CalculatePreviewSize(new(doc.Width, doc.Height));
         PreviewBitmap = new WriteableBitmap(previewSize.X, previewSize.Y, 96, 96, PixelFormats.Pbgra32, null);
         PreviewSurface = SKSurface.Create(new SKImageInfo(previewSize.X, previewSize.Y, SKColorType.Bgra8888), PreviewBitmap.BackBuffer, PreviewBitmap.BackBufferStride);
-        if (member.ReadOnlyMask is not null)
+        if (member.Mask is not null)
         {
             MaskPreviewBitmap = new WriteableBitmap(previewSize.X, previewSize.Y, 96, 96, PixelFormats.Pbgra32, null);
             MaskPreviewSurface = SKSurface.Create(new SKImageInfo(previewSize.X, previewSize.Y, SKColorType.Bgra8888), PreviewBitmap.BackBuffer, PreviewBitmap.BackBufferStride);