Browse Source

Zoombox impl

Equbuxu 3 years ago
parent
commit
126c00e497
100 changed files with 1133 additions and 519 deletions
  1. 0 6
      src/ChangeableDocument/Actions/IAction.cs
  2. 0 5
      src/ChangeableDocument/Actions/Undo/Redo_Action.cs
  3. 0 5
      src/ChangeableDocument/Actions/Undo/Undo_Action.cs
  4. 0 6
      src/ChangeableDocument/ChangeInfos/IChangeInfo.cs
  5. 0 8
      src/ChangeableDocument/Changeables/IChangeable.cs
  6. 0 10
      src/ChangeableDocument/Changes/UpdateableChange.cs
  7. 4 0
      src/ChunkyImageLib/DataHolders/Vector2d.cs
  8. 3 1
      src/ChunkyImageLib/Surface.cs
  9. 3 3
      src/PixiEditor.ChangeableDocument/Actions/Document/ResizeCanvas_Action.cs
  10. 3 3
      src/PixiEditor.ChangeableDocument/Actions/Drawing/CombineStructureMembersOnto_Action.cs
  11. 4 4
      src/PixiEditor.ChangeableDocument/Actions/Drawing/Rectangle/DrawRectangle_Action.cs
  12. 3 3
      src/PixiEditor.ChangeableDocument/Actions/Drawing/Rectangle/EndDrawRectangle_Action.cs
  13. 3 3
      src/PixiEditor.ChangeableDocument/Actions/Drawing/Selection/ClearSelection_Action.cs
  14. 3 3
      src/PixiEditor.ChangeableDocument/Actions/Drawing/Selection/EndSelectRectangle_Action.cs
  15. 4 4
      src/PixiEditor.ChangeableDocument/Actions/Drawing/Selection/SelectRectangle_Action.cs
  16. 6 0
      src/PixiEditor.ChangeableDocument/Actions/IAction.cs
  17. 2 2
      src/PixiEditor.ChangeableDocument/Actions/IEndChangeAction.cs
  18. 2 2
      src/PixiEditor.ChangeableDocument/Actions/IMakeChangeAction.cs
  19. 2 2
      src/PixiEditor.ChangeableDocument/Actions/IStartOrUpdateChangeAction.cs
  20. 2 2
      src/PixiEditor.ChangeableDocument/Actions/Properties/EndOpacityChange_Action.cs
  21. 2 2
      src/PixiEditor.ChangeableDocument/Actions/Properties/OpacityChange_Action.cs
  22. 2 2
      src/PixiEditor.ChangeableDocument/Actions/Properties/SetStructureMemberName_Action.cs
  23. 2 2
      src/PixiEditor.ChangeableDocument/Actions/Properties/SetStructureMemberVisibility_Action.cs
  24. 2 2
      src/PixiEditor.ChangeableDocument/Actions/Structure/CreateStructureMember_Action.cs
  25. 2 2
      src/PixiEditor.ChangeableDocument/Actions/Structure/DeleteStructureMember_Action.cs
  26. 2 2
      src/PixiEditor.ChangeableDocument/Actions/Structure/MoveStructureMember_Action.cs
  27. 1 1
      src/PixiEditor.ChangeableDocument/Actions/Undo/DeleteRecordedChanges_Action.cs
  28. 1 1
      src/PixiEditor.ChangeableDocument/Actions/Undo/MergeLatestChanges_Action.cs
  29. 5 0
      src/PixiEditor.ChangeableDocument/Actions/Undo/Redo_Action.cs
  30. 5 0
      src/PixiEditor.ChangeableDocument/Actions/Undo/Undo_Action.cs
  31. 1 1
      src/PixiEditor.ChangeableDocument/ChangeInfos/CreateStructureMember_ChangeInfo.cs
  32. 1 1
      src/PixiEditor.ChangeableDocument/ChangeInfos/DeleteStructureMember_ChangeInfo.cs
  33. 6 0
      src/PixiEditor.ChangeableDocument/ChangeInfos/IChangeInfo.cs
  34. 1 1
      src/PixiEditor.ChangeableDocument/ChangeInfos/LayerImageChunks_ChangeInfo.cs
  35. 1 1
      src/PixiEditor.ChangeableDocument/ChangeInfos/MoveStructureMember_ChangeInfo.cs
  36. 1 1
      src/PixiEditor.ChangeableDocument/ChangeInfos/Selection_ChangeInfo.cs
  37. 1 1
      src/PixiEditor.ChangeableDocument/ChangeInfos/Size_ChangeInfo.cs
  38. 1 1
      src/PixiEditor.ChangeableDocument/ChangeInfos/StructureMemberIsVisible_ChangeInfo.cs
  39. 1 1
      src/PixiEditor.ChangeableDocument/ChangeInfos/StructureMemberName_ChangeInfo.cs
  40. 1 1
      src/PixiEditor.ChangeableDocument/ChangeInfos/StructureMemberOpacity_ChangeInfo.cs
  41. 3 3
      src/PixiEditor.ChangeableDocument/Changeables/Document.cs
  42. 2 2
      src/PixiEditor.ChangeableDocument/Changeables/Folder.cs
  43. 8 0
      src/PixiEditor.ChangeableDocument/Changeables/IChangeable.cs
  44. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyDocument.cs
  45. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyFolder.cs
  46. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyLayer.cs
  47. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlySelection.cs
  48. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyStructureMember.cs
  49. 3 3
      src/PixiEditor.ChangeableDocument/Changeables/Layer.cs
  50. 3 3
      src/PixiEditor.ChangeableDocument/Changeables/Selection.cs
  51. 2 2
      src/PixiEditor.ChangeableDocument/Changeables/StructureMember.cs
  52. 3 3
      src/PixiEditor.ChangeableDocument/Changes/Change.cs
  53. 3 3
      src/PixiEditor.ChangeableDocument/Changes/CreateStructureMember_Change.cs
  54. 3 3
      src/PixiEditor.ChangeableDocument/Changes/DeleteStructureMember_Change.cs
  55. 4 4
      src/PixiEditor.ChangeableDocument/Changes/Drawing/ClearSelection_Change.cs
  56. 5 5
      src/PixiEditor.ChangeableDocument/Changes/Drawing/CombineStructureMembersOnto_Change.cs
  57. 4 4
      src/PixiEditor.ChangeableDocument/Changes/Drawing/DrawRectangle_UpdateableChange.cs
  58. 4 4
      src/PixiEditor.ChangeableDocument/Changes/Drawing/SelectRectangle_UpdateableChange.cs
  59. 3 3
      src/PixiEditor.ChangeableDocument/Changes/MoveStructureMember_Change.cs
  60. 4 4
      src/PixiEditor.ChangeableDocument/Changes/ResizeCanvas_Change.cs
  61. 3 3
      src/PixiEditor.ChangeableDocument/Changes/StructureMemberIsVisible_Change.cs
  62. 3 3
      src/PixiEditor.ChangeableDocument/Changes/StructureMemberName_Change.cs
  63. 3 3
      src/PixiEditor.ChangeableDocument/Changes/StructureMemberOpacity_UpdateableChange.cs
  64. 10 0
      src/PixiEditor.ChangeableDocument/Changes/UpdateableChange.cs
  65. 6 6
      src/PixiEditor.ChangeableDocument/DocumentChangeTracker.cs
  66. 0 0
      src/PixiEditor.ChangeableDocument/PixiEditor.ChangeableDocument.csproj
  67. 3 3
      src/PixiEditor.ChangeableDocument/Rendering/ChunkRenderer.cs
  68. 1 1
      src/PixiEditor.ChangeableDocument/StructureMemberType.cs
  69. 10 0
      src/PixiEditor.Zoombox/AssemblyInfo.cs
  70. 13 0
      src/PixiEditor.Zoombox/IDragOperation.cs
  71. 33 0
      src/PixiEditor.Zoombox/MoveDragOperation.cs
  72. 13 0
      src/PixiEditor.Zoombox/PixiEditor.Zoombox.csproj
  73. 21 0
      src/PixiEditor.Zoombox/ViewportRoutedEventArgs.cs
  74. 47 0
      src/PixiEditor.Zoombox/ZoomDragOperation.cs
  75. 20 0
      src/PixiEditor.Zoombox/Zoombox.xaml
  76. 308 0
      src/PixiEditor.Zoombox/Zoombox.xaml.cs
  77. 9 0
      src/PixiEditor.Zoombox/ZoomboxMode.cs
  78. 7 1
      src/PixiEditorPrototype.sln
  79. 2 2
      src/PixiEditorPrototype/App.xaml
  80. 1 1
      src/PixiEditorPrototype/Behaviors/SliderUpdateBehavior.cs
  81. 0 13
      src/PixiEditorPrototype/MainWindow.xaml
  82. 0 18
      src/PixiEditorPrototype/MainWindow.xaml.cs
  83. 12 14
      src/PixiEditorPrototype/Models/ActionAccumulator.cs
  84. 22 0
      src/PixiEditorPrototype/Models/DocumentHelpers.cs
  85. 12 0
      src/PixiEditorPrototype/Models/DocumentState.cs
  86. 11 9
      src/PixiEditorPrototype/Models/DocumentStructureHelper.cs
  87. 40 24
      src/PixiEditorPrototype/Models/DocumentUpdater.cs
  88. 12 9
      src/PixiEditorPrototype/Models/MoveViewport_PassthroughAction.cs
  89. 28 28
      src/PixiEditorPrototype/Models/Rendering/WriteableBitmapUpdater.cs
  90. 1 1
      src/PixiEditorPrototype/Models/Tool.cs
  91. 2 1
      src/PixiEditorPrototype/PixiEditorPrototype.csproj
  92. 87 136
      src/PixiEditorPrototype/ViewModels/DocumentViewModel.cs
  93. 3 2
      src/PixiEditorPrototype/ViewModels/FolderViewModel.cs
  94. 3 2
      src/PixiEditorPrototype/ViewModels/LayerViewModel.cs
  95. 12 9
      src/PixiEditorPrototype/ViewModels/StructureMemberViewModel.cs
  96. 134 0
      src/PixiEditorPrototype/ViewModels/ViewModelMain.cs
  97. 0 26
      src/PixiEditorPrototype/Views/DocumentView.xaml.cs
  98. 1 1
      src/PixiEditorPrototype/Views/IMainView.cs
  99. 79 57
      src/PixiEditorPrototype/Views/MainWindow.xaml
  100. 19 0
      src/PixiEditorPrototype/Views/MainWindow.xaml.cs

+ 0 - 6
src/ChangeableDocument/Actions/IAction.cs

@@ -1,6 +0,0 @@
-namespace ChangeableDocument.Actions
-{
-    public interface IAction
-    {
-    }
-}

+ 0 - 5
src/ChangeableDocument/Actions/Undo/Redo_Action.cs

@@ -1,5 +0,0 @@
-namespace ChangeableDocument.Actions.Undo;
-
-public record class Redo_Action : IAction
-{
-}

+ 0 - 5
src/ChangeableDocument/Actions/Undo/Undo_Action.cs

@@ -1,5 +0,0 @@
-namespace ChangeableDocument.Actions.Undo;
-
-public record class Undo_Action : IAction
-{
-}

+ 0 - 6
src/ChangeableDocument/ChangeInfos/IChangeInfo.cs

@@ -1,6 +0,0 @@
-namespace ChangeableDocument.ChangeInfos
-{
-    public interface IChangeInfo
-    {
-    }
-}

+ 0 - 8
src/ChangeableDocument/Changeables/IChangeable.cs

@@ -1,8 +0,0 @@
-namespace ChangeableDocument.Changeables
-{
-
-    internal interface IChangeable
-    {
-
-    };
-}

+ 0 - 10
src/ChangeableDocument/Changes/UpdateableChange.cs

@@ -1,10 +0,0 @@
-using ChangeableDocument.Changeables;
-using ChangeableDocument.ChangeInfos;
-
-namespace ChangeableDocument.Changes
-{
-    internal abstract class UpdateableChange : Change
-    {
-        public abstract IChangeInfo? ApplyTemporarily(Document target);
-    }
-}

+ 4 - 0
src/ChunkyImageLib/DataHolders/Vector2d.cs

@@ -72,6 +72,10 @@ namespace ChunkyImageLib.DataHolders
         {
             return new Vector2d(X * other.X, Y * other.Y);
         }
+        public Vector2d Divide(Vector2d other)
+        {
+            return new Vector2d(X / other.X, Y / other.Y);
+        }
         public static Vector2d operator +(Vector2d a, Vector2d b)
         {
             return new Vector2d(a.X + b.X, a.Y + b.Y);

+ 3 - 1
src/ChunkyImageLib/Surface.cs

@@ -42,9 +42,11 @@ namespace ChunkyImageLib
 
         public unsafe SKColor GetSRGBPixel(int x, int y)
         {
+            throw new NotImplementedException("This function needs to be changed to account for linear -> srgb conversion");
+            /*
             Half* ptr = (Half*)(PixelBuffer + (x + y * Size.X) * bytesPerPixel);
             float a = (float)ptr[3];
-            return (SKColor)new SKColorF((float)ptr[0] / a, (float)ptr[1] / a, (float)ptr[2] / a, (float)ptr[3]);
+            return (SKColor)new SKColorF((float)ptr[0] / a, (float)ptr[1] / a, (float)ptr[2] / a, (float)ptr[3]);*/
         }
 
         public void SaveToDesktop(string filename = "savedSurface.png")

+ 3 - 3
src/ChangeableDocument/Actions/Document/ResizeCanvas_Action.cs → src/PixiEditor.ChangeableDocument/Actions/Document/ResizeCanvas_Action.cs

@@ -1,7 +1,7 @@
-using ChangeableDocument.Changes;
-using ChunkyImageLib.DataHolders;
+using ChunkyImageLib.DataHolders;
+using PixiEditor.ChangeableDocument.Changes;
 
-namespace ChangeableDocument.Actions.Document
+namespace PixiEditor.ChangeableDocument.Actions.Document
 {
     public record class ResizeCanvas_Action : IMakeChangeAction
     {

+ 3 - 3
src/ChangeableDocument/Actions/Drawing/CombineStructureMembersOnto_Action.cs → src/PixiEditor.ChangeableDocument/Actions/Drawing/CombineStructureMembersOnto_Action.cs

@@ -1,7 +1,7 @@
-using ChangeableDocument.Changes;
-using ChangeableDocument.Changes.Drawing;
+using PixiEditor.ChangeableDocument.Changes;
+using PixiEditor.ChangeableDocument.Changes.Drawing;
 
-namespace ChangeableDocument.Actions.Drawing
+namespace PixiEditor.ChangeableDocument.Actions.Drawing
 {
     public record class CombineStructureMembersOnto_Action : IMakeChangeAction
     {

+ 4 - 4
src/ChangeableDocument/Actions/Drawing/Rectangle/DrawRectangle_Action.cs → src/PixiEditor.ChangeableDocument/Actions/Drawing/Rectangle/DrawRectangle_Action.cs

@@ -1,8 +1,8 @@
-using ChangeableDocument.Changes;
-using ChangeableDocument.Changes.Drawing;
-using ChunkyImageLib.DataHolders;
+using ChunkyImageLib.DataHolders;
+using PixiEditor.ChangeableDocument.Changes;
+using PixiEditor.ChangeableDocument.Changes.Drawing;
 
-namespace ChangeableDocument.Actions.Drawing.Rectangle
+namespace PixiEditor.ChangeableDocument.Actions.Drawing.Rectangle
 {
     public record class DrawRectangle_Action : IStartOrUpdateChangeAction
     {

+ 3 - 3
src/ChangeableDocument/Actions/Drawing/Rectangle/EndDrawRectangle_Action.cs → src/PixiEditor.ChangeableDocument/Actions/Drawing/Rectangle/EndDrawRectangle_Action.cs

@@ -1,7 +1,7 @@
-using ChangeableDocument.Changes;
-using ChangeableDocument.Changes.Drawing;
+using PixiEditor.ChangeableDocument.Changes;
+using PixiEditor.ChangeableDocument.Changes.Drawing;
 
-namespace ChangeableDocument.Actions.Drawing.Rectangle
+namespace PixiEditor.ChangeableDocument.Actions.Drawing.Rectangle
 {
     public record class EndDrawRectangle_Action : IEndChangeAction
     {

+ 3 - 3
src/ChangeableDocument/Actions/Drawing/Selection/ClearSelection_Action.cs → src/PixiEditor.ChangeableDocument/Actions/Drawing/Selection/ClearSelection_Action.cs

@@ -1,7 +1,7 @@
-using ChangeableDocument.Changes;
-using ChangeableDocument.Changes.Drawing;
+using PixiEditor.ChangeableDocument.Changes;
+using PixiEditor.ChangeableDocument.Changes.Drawing;
 
-namespace ChangeableDocument.Actions.Drawing.Selection
+namespace PixiEditor.ChangeableDocument.Actions.Drawing.Selection
 {
     public record class ClearSelection_Action : IMakeChangeAction
     {

+ 3 - 3
src/ChangeableDocument/Actions/Drawing/Selection/EndSelectRectangle_Action.cs → src/PixiEditor.ChangeableDocument/Actions/Drawing/Selection/EndSelectRectangle_Action.cs

@@ -1,7 +1,7 @@
-using ChangeableDocument.Changes;
-using ChangeableDocument.Changes.Drawing;
+using PixiEditor.ChangeableDocument.Changes;
+using PixiEditor.ChangeableDocument.Changes.Drawing;
 
-namespace ChangeableDocument.Actions.Drawing.Selection
+namespace PixiEditor.ChangeableDocument.Actions.Drawing.Selection
 {
     public record class EndSelectRectangle_Action : IEndChangeAction
     {

+ 4 - 4
src/ChangeableDocument/Actions/Drawing/Selection/SelectRectangle_Action.cs → src/PixiEditor.ChangeableDocument/Actions/Drawing/Selection/SelectRectangle_Action.cs

@@ -1,8 +1,8 @@
-using ChangeableDocument.Changes;
-using ChangeableDocument.Changes.Drawing;
-using ChunkyImageLib.DataHolders;
+using ChunkyImageLib.DataHolders;
+using PixiEditor.ChangeableDocument.Changes;
+using PixiEditor.ChangeableDocument.Changes.Drawing;
 
-namespace ChangeableDocument.Actions.Drawing.Selection
+namespace PixiEditor.ChangeableDocument.Actions.Drawing.Selection
 {
     public record class SelectRectangle_Action : IStartOrUpdateChangeAction
     {

+ 6 - 0
src/PixiEditor.ChangeableDocument/Actions/IAction.cs

@@ -0,0 +1,6 @@
+namespace PixiEditor.ChangeableDocument.Actions
+{
+    public interface IAction
+    {
+    }
+}

+ 2 - 2
src/ChangeableDocument/Actions/IEndChangeAction.cs → src/PixiEditor.ChangeableDocument/Actions/IEndChangeAction.cs

@@ -1,6 +1,6 @@
-using ChangeableDocument.Changes;
+using PixiEditor.ChangeableDocument.Changes;
 
-namespace ChangeableDocument.Actions
+namespace PixiEditor.ChangeableDocument.Actions
 {
     internal interface IEndChangeAction : IAction
     {

+ 2 - 2
src/ChangeableDocument/Actions/IMakeChangeAction.cs → src/PixiEditor.ChangeableDocument/Actions/IMakeChangeAction.cs

@@ -1,6 +1,6 @@
-using ChangeableDocument.Changes;
+using PixiEditor.ChangeableDocument.Changes;
 
-namespace ChangeableDocument.Actions
+namespace PixiEditor.ChangeableDocument.Actions
 {
     internal interface IMakeChangeAction : IAction
     {

+ 2 - 2
src/ChangeableDocument/Actions/IStartOrUpdateChangeAction.cs → src/PixiEditor.ChangeableDocument/Actions/IStartOrUpdateChangeAction.cs

@@ -1,6 +1,6 @@
-using ChangeableDocument.Changes;
+using PixiEditor.ChangeableDocument.Changes;
 
-namespace ChangeableDocument.Actions
+namespace PixiEditor.ChangeableDocument.Actions
 {
     internal interface IStartOrUpdateChangeAction : IAction
     {

+ 2 - 2
src/ChangeableDocument/Actions/Properties/EndOpacityChange_Action.cs → src/PixiEditor.ChangeableDocument/Actions/Properties/EndOpacityChange_Action.cs

@@ -1,6 +1,6 @@
-using ChangeableDocument.Changes;
+using PixiEditor.ChangeableDocument.Changes;
 
-namespace ChangeableDocument.Actions.Properties
+namespace PixiEditor.ChangeableDocument.Actions.Properties
 {
     public record class EndOpacityChange_Action : IEndChangeAction
     {

+ 2 - 2
src/ChangeableDocument/Actions/Properties/OpacityChange_Action.cs → src/PixiEditor.ChangeableDocument/Actions/Properties/OpacityChange_Action.cs

@@ -1,6 +1,6 @@
-using ChangeableDocument.Changes;
+using PixiEditor.ChangeableDocument.Changes;
 
-namespace ChangeableDocument.Actions.Properties
+namespace PixiEditor.ChangeableDocument.Actions.Properties
 {
     public record class OpacityChange_Action : IStartOrUpdateChangeAction
     {

+ 2 - 2
src/ChangeableDocument/Actions/Properties/SetStructureMemberName_Action.cs → src/PixiEditor.ChangeableDocument/Actions/Properties/SetStructureMemberName_Action.cs

@@ -1,6 +1,6 @@
-using ChangeableDocument.Changes;
+using PixiEditor.ChangeableDocument.Changes;
 
-namespace ChangeableDocument.Actions.Properties;
+namespace PixiEditor.ChangeableDocument.Actions.Properties;
 
 public record class SetStructureMemberName_Action : IMakeChangeAction
 {

+ 2 - 2
src/ChangeableDocument/Actions/Properties/SetStructureMemberVisibility_Action.cs → src/PixiEditor.ChangeableDocument/Actions/Properties/SetStructureMemberVisibility_Action.cs

@@ -1,6 +1,6 @@
-using ChangeableDocument.Changes;
+using PixiEditor.ChangeableDocument.Changes;
 
-namespace ChangeableDocument.Actions.Properties;
+namespace PixiEditor.ChangeableDocument.Actions.Properties;
 
 public record class SetStructureMemberVisibility_Action : IMakeChangeAction
 {

+ 2 - 2
src/ChangeableDocument/Actions/Structure/CreateStructureMember_Action.cs → src/PixiEditor.ChangeableDocument/Actions/Structure/CreateStructureMember_Action.cs

@@ -1,6 +1,6 @@
-using ChangeableDocument.Changes;
+using PixiEditor.ChangeableDocument.Changes;
 
-namespace ChangeableDocument.Actions.Structure;
+namespace PixiEditor.ChangeableDocument.Actions.Structure;
 
 public record class CreateStructureMember_Action : IMakeChangeAction
 {

+ 2 - 2
src/ChangeableDocument/Actions/Structure/DeleteStructureMember_Action.cs → src/PixiEditor.ChangeableDocument/Actions/Structure/DeleteStructureMember_Action.cs

@@ -1,6 +1,6 @@
-using ChangeableDocument.Changes;
+using PixiEditor.ChangeableDocument.Changes;
 
-namespace ChangeableDocument.Actions.Structure;
+namespace PixiEditor.ChangeableDocument.Actions.Structure;
 
 public record class DeleteStructureMember_Action : IMakeChangeAction
 {

+ 2 - 2
src/ChangeableDocument/Actions/Structure/MoveStructureMember_Action.cs → src/PixiEditor.ChangeableDocument/Actions/Structure/MoveStructureMember_Action.cs

@@ -1,6 +1,6 @@
-using ChangeableDocument.Changes;
+using PixiEditor.ChangeableDocument.Changes;
 
-namespace ChangeableDocument.Actions.Structure;
+namespace PixiEditor.ChangeableDocument.Actions.Structure;
 
 public record class MoveStructureMember_Action : IMakeChangeAction
 {

+ 1 - 1
src/ChangeableDocument/Actions/Undo/DeleteRecordedChanges_Action.cs → src/PixiEditor.ChangeableDocument/Actions/Undo/DeleteRecordedChanges_Action.cs

@@ -1,4 +1,4 @@
-namespace ChangeableDocument.Actions.Undo
+namespace PixiEditor.ChangeableDocument.Actions.Undo
 {
     public record class DeleteRecordedChanges_Action : IAction
     {

+ 1 - 1
src/ChangeableDocument/Actions/Undo/MergeLatestChanges_Action.cs → src/PixiEditor.ChangeableDocument/Actions/Undo/MergeLatestChanges_Action.cs

@@ -1,4 +1,4 @@
-namespace ChangeableDocument.Actions.Undo
+namespace PixiEditor.ChangeableDocument.Actions.Undo
 {
     public record class MergeLatestChanges_Action : IAction
     {

+ 5 - 0
src/PixiEditor.ChangeableDocument/Actions/Undo/Redo_Action.cs

@@ -0,0 +1,5 @@
+namespace PixiEditor.ChangeableDocument.Actions.Undo;
+
+public record class Redo_Action : IAction
+{
+}

+ 5 - 0
src/PixiEditor.ChangeableDocument/Actions/Undo/Undo_Action.cs

@@ -0,0 +1,5 @@
+namespace PixiEditor.ChangeableDocument.Actions.Undo;
+
+public record class Undo_Action : IAction
+{
+}

+ 1 - 1
src/ChangeableDocument/ChangeInfos/CreateStructureMember_ChangeInfo.cs → src/PixiEditor.ChangeableDocument/ChangeInfos/CreateStructureMember_ChangeInfo.cs

@@ -1,4 +1,4 @@
-namespace ChangeableDocument.ChangeInfos
+namespace PixiEditor.ChangeableDocument.ChangeInfos
 {
     public record class CreateStructureMember_ChangeInfo : IChangeInfo
     {

+ 1 - 1
src/ChangeableDocument/ChangeInfos/DeleteStructureMember_ChangeInfo.cs → src/PixiEditor.ChangeableDocument/ChangeInfos/DeleteStructureMember_ChangeInfo.cs

@@ -1,4 +1,4 @@
-namespace ChangeableDocument.ChangeInfos
+namespace PixiEditor.ChangeableDocument.ChangeInfos
 {
     public record class DeleteStructureMember_ChangeInfo : IChangeInfo
     {

+ 6 - 0
src/PixiEditor.ChangeableDocument/ChangeInfos/IChangeInfo.cs

@@ -0,0 +1,6 @@
+namespace PixiEditor.ChangeableDocument.ChangeInfos
+{
+    public interface IChangeInfo
+    {
+    }
+}

+ 1 - 1
src/ChangeableDocument/ChangeInfos/LayerImageChunks_ChangeInfo.cs → src/PixiEditor.ChangeableDocument/ChangeInfos/LayerImageChunks_ChangeInfo.cs

@@ -1,6 +1,6 @@
 using ChunkyImageLib.DataHolders;
 
-namespace ChangeableDocument.ChangeInfos
+namespace PixiEditor.ChangeableDocument.ChangeInfos
 {
     public record class LayerImageChunks_ChangeInfo : IChangeInfo
     {

+ 1 - 1
src/ChangeableDocument/ChangeInfos/MoveStructureMember_ChangeInfo.cs → src/PixiEditor.ChangeableDocument/ChangeInfos/MoveStructureMember_ChangeInfo.cs

@@ -1,4 +1,4 @@
-namespace ChangeableDocument.ChangeInfos
+namespace PixiEditor.ChangeableDocument.ChangeInfos
 {
     public record class MoveStructureMember_ChangeInfo : IChangeInfo
     {

+ 1 - 1
src/ChangeableDocument/ChangeInfos/Selection_ChangeInfo.cs → src/PixiEditor.ChangeableDocument/ChangeInfos/Selection_ChangeInfo.cs

@@ -1,6 +1,6 @@
 using ChunkyImageLib.DataHolders;
 
-namespace ChangeableDocument.ChangeInfos
+namespace PixiEditor.ChangeableDocument.ChangeInfos
 {
     public record class Selection_ChangeInfo : IChangeInfo
     {

+ 1 - 1
src/ChangeableDocument/ChangeInfos/Size_ChangeInfo.cs → src/PixiEditor.ChangeableDocument/ChangeInfos/Size_ChangeInfo.cs

@@ -1,4 +1,4 @@
-namespace ChangeableDocument.ChangeInfos
+namespace PixiEditor.ChangeableDocument.ChangeInfos
 {
     public record class Size_ChangeInfo : IChangeInfo
     {

+ 1 - 1
src/ChangeableDocument/ChangeInfos/StructureMemberIsVisible_ChangeInfo.cs → src/PixiEditor.ChangeableDocument/ChangeInfos/StructureMemberIsVisible_ChangeInfo.cs

@@ -1,4 +1,4 @@
-namespace ChangeableDocument.ChangeInfos
+namespace PixiEditor.ChangeableDocument.ChangeInfos
 {
     public record class StructureMemberIsVisible_ChangeInfo : IChangeInfo
     {

+ 1 - 1
src/ChangeableDocument/ChangeInfos/StructureMemberName_ChangeInfo.cs → src/PixiEditor.ChangeableDocument/ChangeInfos/StructureMemberName_ChangeInfo.cs

@@ -1,4 +1,4 @@
-namespace ChangeableDocument.ChangeInfos
+namespace PixiEditor.ChangeableDocument.ChangeInfos
 {
     public record class StructureMemberName_ChangeInfo : IChangeInfo
     {

+ 1 - 1
src/ChangeableDocument/ChangeInfos/StructureMemberOpacity_ChangeInfo.cs → src/PixiEditor.ChangeableDocument/ChangeInfos/StructureMemberOpacity_ChangeInfo.cs

@@ -1,4 +1,4 @@
-namespace ChangeableDocument.ChangeInfos
+namespace PixiEditor.ChangeableDocument.ChangeInfos
 {
     public record class StructureMemberOpacity_ChangeInfo : IChangeInfo
     {

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

@@ -1,7 +1,7 @@
-using ChangeableDocument.Changeables.Interfaces;
-using ChunkyImageLib.DataHolders;
+using ChunkyImageLib.DataHolders;
+using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 
-namespace ChangeableDocument.Changeables
+namespace PixiEditor.ChangeableDocument.Changeables
 {
     internal class Document : IChangeable, IReadOnlyDocument
     {

+ 2 - 2
src/ChangeableDocument/Changeables/Folder.cs → src/PixiEditor.ChangeableDocument/Changeables/Folder.cs

@@ -1,6 +1,6 @@
-using ChangeableDocument.Changeables.Interfaces;
+using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 
-namespace ChangeableDocument.Changeables
+namespace PixiEditor.ChangeableDocument.Changeables
 {
     internal class Folder : StructureMember, IReadOnlyFolder
     {

+ 8 - 0
src/PixiEditor.ChangeableDocument/Changeables/IChangeable.cs

@@ -0,0 +1,8 @@
+namespace PixiEditor.ChangeableDocument.Changeables
+{
+
+    internal interface IChangeable
+    {
+
+    };
+}

+ 1 - 1
src/ChangeableDocument/Changeables/Interfaces/IReadOnlyDocument.cs → src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyDocument.cs

@@ -1,6 +1,6 @@
 using ChunkyImageLib.DataHolders;
 
-namespace ChangeableDocument.Changeables.Interfaces
+namespace PixiEditor.ChangeableDocument.Changeables.Interfaces
 {
     public interface IReadOnlyDocument
     {

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

@@ -1,4 +1,4 @@
-namespace ChangeableDocument.Changeables.Interfaces
+namespace PixiEditor.ChangeableDocument.Changeables.Interfaces
 {
     public interface IReadOnlyFolder : IReadOnlyStructureMember
     {

+ 1 - 1
src/ChangeableDocument/Changeables/Interfaces/IReadOnlyLayer.cs → src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyLayer.cs

@@ -1,6 +1,6 @@
 using ChunkyImageLib;
 
-namespace ChangeableDocument.Changeables.Interfaces
+namespace PixiEditor.ChangeableDocument.Changeables.Interfaces
 {
     public interface IReadOnlyLayer : IReadOnlyStructureMember
     {

+ 1 - 1
src/ChangeableDocument/Changeables/Interfaces/IReadOnlySelection.cs → src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlySelection.cs

@@ -1,6 +1,6 @@
 using ChunkyImageLib;
 
-namespace ChangeableDocument.Changeables.Interfaces
+namespace PixiEditor.ChangeableDocument.Changeables.Interfaces
 {
     public interface IReadOnlySelection
     {

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

@@ -1,4 +1,4 @@
-namespace ChangeableDocument.Changeables.Interfaces
+namespace PixiEditor.ChangeableDocument.Changeables.Interfaces
 {
     public interface IReadOnlyStructureMember
     {

+ 3 - 3
src/ChangeableDocument/Changeables/Layer.cs → src/PixiEditor.ChangeableDocument/Changeables/Layer.cs

@@ -1,8 +1,8 @@
-using ChangeableDocument.Changeables.Interfaces;
-using ChunkyImageLib;
+using ChunkyImageLib;
 using ChunkyImageLib.DataHolders;
+using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 
-namespace ChangeableDocument.Changeables
+namespace PixiEditor.ChangeableDocument.Changeables
 {
     internal class Layer : StructureMember, IReadOnlyLayer
     {

+ 3 - 3
src/ChangeableDocument/Changeables/Selection.cs → src/PixiEditor.ChangeableDocument/Changeables/Selection.cs

@@ -1,8 +1,8 @@
-using ChangeableDocument.Changeables.Interfaces;
-using ChunkyImageLib;
+using ChunkyImageLib;
+using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using SkiaSharp;
 
-namespace ChangeableDocument.Changeables
+namespace PixiEditor.ChangeableDocument.Changeables
 {
     internal class Selection : IReadOnlySelection
     {

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

@@ -1,6 +1,6 @@
-using ChangeableDocument.Changeables.Interfaces;
+using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 
-namespace ChangeableDocument.Changeables
+namespace PixiEditor.ChangeableDocument.Changeables
 {
     internal abstract class StructureMember : IChangeable, IReadOnlyStructureMember, IDisposable
     {

+ 3 - 3
src/ChangeableDocument/Changes/Change.cs → src/PixiEditor.ChangeableDocument/Changes/Change.cs

@@ -1,7 +1,7 @@
-using ChangeableDocument.Changeables;
-using ChangeableDocument.ChangeInfos;
+using PixiEditor.ChangeableDocument.Changeables;
+using PixiEditor.ChangeableDocument.ChangeInfos;
 
-namespace ChangeableDocument.Changes
+namespace PixiEditor.ChangeableDocument.Changes
 {
     internal abstract class Change : IDisposable
     {

+ 3 - 3
src/ChangeableDocument/Changes/CreateStructureMember_Change.cs → src/PixiEditor.ChangeableDocument/Changes/CreateStructureMember_Change.cs

@@ -1,7 +1,7 @@
-using ChangeableDocument.Changeables;
-using ChangeableDocument.ChangeInfos;
+using PixiEditor.ChangeableDocument.Changeables;
+using PixiEditor.ChangeableDocument.ChangeInfos;
 
-namespace ChangeableDocument.Changes
+namespace PixiEditor.ChangeableDocument.Changes
 {
     internal class CreateStructureMember_Change : Change
     {

+ 3 - 3
src/ChangeableDocument/Changes/DeleteStructureMember_Change.cs → src/PixiEditor.ChangeableDocument/Changes/DeleteStructureMember_Change.cs

@@ -1,7 +1,7 @@
-using ChangeableDocument.Changeables;
-using ChangeableDocument.ChangeInfos;
+using PixiEditor.ChangeableDocument.Changeables;
+using PixiEditor.ChangeableDocument.ChangeInfos;
 
-namespace ChangeableDocument.Changes
+namespace PixiEditor.ChangeableDocument.Changes
 {
     internal class DeleteStructureMember_Change : Change
     {

+ 4 - 4
src/ChangeableDocument/Changes/Drawing/ClearSelection_Change.cs → src/PixiEditor.ChangeableDocument/Changes/Drawing/ClearSelection_Change.cs

@@ -1,9 +1,9 @@
-using ChangeableDocument.Changeables;
-using ChangeableDocument.ChangeInfos;
-using ChunkyImageLib;
+using ChunkyImageLib;
 using ChunkyImageLib.DataHolders;
+using PixiEditor.ChangeableDocument.Changeables;
+using PixiEditor.ChangeableDocument.ChangeInfos;
 
-namespace ChangeableDocument.Changes.Drawing
+namespace PixiEditor.ChangeableDocument.Changes.Drawing
 {
     internal class ClearSelection_Change : Change
     {

+ 5 - 5
src/ChangeableDocument/Changes/Drawing/CombineStructureMembersOnto_Change.cs → src/PixiEditor.ChangeableDocument/Changes/Drawing/CombineStructureMembersOnto_Change.cs

@@ -1,10 +1,10 @@
-using ChangeableDocument.Changeables;
-using ChangeableDocument.ChangeInfos;
-using ChangeableDocument.Rendering;
-using ChunkyImageLib;
+using ChunkyImageLib;
 using ChunkyImageLib.DataHolders;
+using PixiEditor.ChangeableDocument.Changeables;
+using PixiEditor.ChangeableDocument.ChangeInfos;
+using PixiEditor.ChangeableDocument.Rendering;
 
-namespace ChangeableDocument.Changes.Drawing
+namespace PixiEditor.ChangeableDocument.Changes.Drawing
 {
     internal class CombineStructureMembersOnto_Change : Change
     {

+ 4 - 4
src/ChangeableDocument/Changes/Drawing/DrawRectangle_UpdateableChange.cs → src/PixiEditor.ChangeableDocument/Changes/Drawing/DrawRectangle_UpdateableChange.cs

@@ -1,9 +1,9 @@
-using ChangeableDocument.Changeables;
-using ChangeableDocument.ChangeInfos;
-using ChunkyImageLib;
+using ChunkyImageLib;
 using ChunkyImageLib.DataHolders;
+using PixiEditor.ChangeableDocument.Changeables;
+using PixiEditor.ChangeableDocument.ChangeInfos;
 
-namespace ChangeableDocument.Changes.Drawing
+namespace PixiEditor.ChangeableDocument.Changes.Drawing
 {
     internal class DrawRectangle_UpdateableChange : UpdateableChange
     {

+ 4 - 4
src/ChangeableDocument/Changes/Drawing/SelectRectangle_UpdateableChange.cs → src/PixiEditor.ChangeableDocument/Changes/Drawing/SelectRectangle_UpdateableChange.cs

@@ -1,10 +1,10 @@
-using ChangeableDocument.Changeables;
-using ChangeableDocument.ChangeInfos;
-using ChunkyImageLib;
+using ChunkyImageLib;
 using ChunkyImageLib.DataHolders;
+using PixiEditor.ChangeableDocument.Changeables;
+using PixiEditor.ChangeableDocument.ChangeInfos;
 using SkiaSharp;
 
-namespace ChangeableDocument.Changes.Drawing
+namespace PixiEditor.ChangeableDocument.Changes.Drawing
 {
     internal class SelectRectangle_UpdateableChange : UpdateableChange
     {

+ 3 - 3
src/ChangeableDocument/Changes/MoveStructureMember_Change.cs → src/PixiEditor.ChangeableDocument/Changes/MoveStructureMember_Change.cs

@@ -1,7 +1,7 @@
-using ChangeableDocument.Changeables;
-using ChangeableDocument.ChangeInfos;
+using PixiEditor.ChangeableDocument.Changeables;
+using PixiEditor.ChangeableDocument.ChangeInfos;
 
-namespace ChangeableDocument.Changes
+namespace PixiEditor.ChangeableDocument.Changes
 {
     internal class MoveStructureMember_Change : Change
     {

+ 4 - 4
src/ChangeableDocument/Changes/ResizeCanvas_Change.cs → src/PixiEditor.ChangeableDocument/Changes/ResizeCanvas_Change.cs

@@ -1,9 +1,9 @@
-using ChangeableDocument.Changeables;
-using ChangeableDocument.ChangeInfos;
-using ChunkyImageLib;
+using ChunkyImageLib;
 using ChunkyImageLib.DataHolders;
+using PixiEditor.ChangeableDocument.Changeables;
+using PixiEditor.ChangeableDocument.ChangeInfos;
 
-namespace ChangeableDocument.Changes
+namespace PixiEditor.ChangeableDocument.Changes
 {
     internal class ResizeCanvas_Change : Change
     {

+ 3 - 3
src/ChangeableDocument/Changes/StructureMemberIsVisible_Change.cs → src/PixiEditor.ChangeableDocument/Changes/StructureMemberIsVisible_Change.cs

@@ -1,7 +1,7 @@
-using ChangeableDocument.Changeables;
-using ChangeableDocument.ChangeInfos;
+using PixiEditor.ChangeableDocument.Changeables;
+using PixiEditor.ChangeableDocument.ChangeInfos;
 
-namespace ChangeableDocument.Changes
+namespace PixiEditor.ChangeableDocument.Changes
 {
     internal class StructureMemberIsVisible_Change : Change
     {

+ 3 - 3
src/ChangeableDocument/Changes/StructureMemberName_Change.cs → src/PixiEditor.ChangeableDocument/Changes/StructureMemberName_Change.cs

@@ -1,7 +1,7 @@
-using ChangeableDocument.Changeables;
-using ChangeableDocument.ChangeInfos;
+using PixiEditor.ChangeableDocument.Changeables;
+using PixiEditor.ChangeableDocument.ChangeInfos;
 
-namespace ChangeableDocument.Changes
+namespace PixiEditor.ChangeableDocument.Changes
 {
     internal class StructureMemberName_Change : Change
     {

+ 3 - 3
src/ChangeableDocument/Changes/StructureMemberOpacity_UpdateableChange.cs → src/PixiEditor.ChangeableDocument/Changes/StructureMemberOpacity_UpdateableChange.cs

@@ -1,7 +1,7 @@
-using ChangeableDocument.Changeables;
-using ChangeableDocument.ChangeInfos;
+using PixiEditor.ChangeableDocument.Changeables;
+using PixiEditor.ChangeableDocument.ChangeInfos;
 
-namespace ChangeableDocument.Changes
+namespace PixiEditor.ChangeableDocument.Changes
 {
     internal class StructureMemberOpacity_UpdateableChange : UpdateableChange
     {

+ 10 - 0
src/PixiEditor.ChangeableDocument/Changes/UpdateableChange.cs

@@ -0,0 +1,10 @@
+using PixiEditor.ChangeableDocument.Changeables;
+using PixiEditor.ChangeableDocument.ChangeInfos;
+
+namespace PixiEditor.ChangeableDocument.Changes
+{
+    internal abstract class UpdateableChange : Change
+    {
+        public abstract IChangeInfo? ApplyTemporarily(Document target);
+    }
+}

+ 6 - 6
src/ChangeableDocument/DocumentChangeTracker.cs → src/PixiEditor.ChangeableDocument/DocumentChangeTracker.cs

@@ -1,9 +1,9 @@
-using ChangeableDocument.Actions;
-using ChangeableDocument.Actions.Undo;
-using ChangeableDocument.Changeables;
-using ChangeableDocument.Changeables.Interfaces;
-using ChangeableDocument.ChangeInfos;
-using ChangeableDocument.Changes;
+using PixiEditor.ChangeableDocument.Actions;
+using PixiEditor.ChangeableDocument.Actions.Undo;
+using PixiEditor.ChangeableDocument.Changeables;
+using PixiEditor.ChangeableDocument.Changeables.Interfaces;
+using PixiEditor.ChangeableDocument.ChangeInfos;
+using PixiEditor.ChangeableDocument.Changes;
 
 namespace ChangeableDocument
 {

+ 0 - 0
src/ChangeableDocument/ChangeableDocument.csproj → src/PixiEditor.ChangeableDocument/PixiEditor.ChangeableDocument.csproj


+ 3 - 3
src/ChangeableDocument/Rendering/ChunkRenderer.cs → src/PixiEditor.ChangeableDocument/Rendering/ChunkRenderer.cs

@@ -1,9 +1,9 @@
-using ChangeableDocument.Changeables.Interfaces;
-using ChunkyImageLib;
+using ChunkyImageLib;
 using ChunkyImageLib.DataHolders;
+using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using SkiaSharp;
 
-namespace ChangeableDocument.Rendering
+namespace PixiEditor.ChangeableDocument.Rendering
 {
     public static class ChunkRenderer
     {

+ 1 - 1
src/ChangeableDocument/StructureMemberType.cs → src/PixiEditor.ChangeableDocument/StructureMemberType.cs

@@ -1,4 +1,4 @@
-namespace ChangeableDocument
+namespace PixiEditor.ChangeableDocument
 {
     public enum StructureMemberType
     {

+ 10 - 0
src/PixiEditor.Zoombox/AssemblyInfo.cs

@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+    ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+                                     //(used if a resource is not found in the page,
+                                     // or application resource dictionaries)
+    ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+                                              //(used if a resource is not found in the page,
+                                              // app, or any theme specific resource dictionaries)
+)]

+ 13 - 0
src/PixiEditor.Zoombox/IDragOperation.cs

@@ -0,0 +1,13 @@
+using System.Windows.Input;
+
+namespace PixiEditor.Zoombox
+{
+    internal interface IDragOperation
+    {
+        void Start(MouseButtonEventArgs e);
+
+        void Update(MouseEventArgs e);
+
+        void Terminate();
+    }
+}

+ 33 - 0
src/PixiEditor.Zoombox/MoveDragOperation.cs

@@ -0,0 +1,33 @@
+using System.Windows;
+using System.Windows.Input;
+
+namespace PixiEditor.Zoombox
+{
+    internal class MoveDragOperation : IDragOperation
+    {
+        private Zoombox parent;
+        private Point prevMousePos;
+
+        public MoveDragOperation(Zoombox zoomBox)
+        {
+            parent = zoomBox;
+        }
+        public void Start(MouseButtonEventArgs e)
+        {
+            prevMousePos = e.GetPosition(parent.mainCanvas);
+            parent.mainCanvas.CaptureMouse();
+        }
+
+        public void Update(MouseEventArgs e)
+        {
+            var curMousePos = e.GetPosition(parent.mainCanvas);
+            parent.SpaceOriginPos += curMousePos - prevMousePos;
+            prevMousePos = curMousePos;
+        }
+
+        public void Terminate()
+        {
+            parent.mainCanvas.ReleaseMouseCapture();
+        }
+    }
+}

+ 13 - 0
src/PixiEditor.Zoombox/PixiEditor.Zoombox.csproj

@@ -0,0 +1,13 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net6.0-windows</TargetFramework>
+    <Nullable>enable</Nullable>
+    <UseWPF>true</UseWPF>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\ChunkyImageLib\ChunkyImageLib.csproj" />
+  </ItemGroup>
+
+</Project>

+ 21 - 0
src/PixiEditor.Zoombox/ViewportRoutedEventArgs.cs

@@ -0,0 +1,21 @@
+using ChunkyImageLib.DataHolders;
+using System.Windows;
+
+namespace PixiEditor.Zoombox
+{
+    public class ViewportRoutedEventArgs : RoutedEventArgs
+    {
+        public ViewportRoutedEventArgs(RoutedEvent e, Vector2d center, Vector2d size, Vector2d realSize, double angle) : base(e)
+        {
+            Center = center;
+            Size = size;
+            RealSize = realSize;
+            Angle = angle;
+        }
+
+        public Vector2d Center { get; }
+        public Vector2d Size { get; }
+        public Vector2d RealSize { get; }
+        public double Angle { get; }
+    }
+}

+ 47 - 0
src/PixiEditor.Zoombox/ZoomDragOperation.cs

@@ -0,0 +1,47 @@
+using System.Windows;
+using System.Windows.Input;
+
+namespace PixiEditor.Zoombox
+{
+    internal class ZoomDragOperation : IDragOperation
+    {
+        private Zoombox parent;
+
+        private double initZoomPower;
+        private Point initSpaceOriginPos;
+
+        private Point zoomOrigin;
+        private Point screenZoomOrigin;
+
+        public ZoomDragOperation(Zoombox zoomBox)
+        {
+            parent = zoomBox;
+        }
+        public void Start(MouseButtonEventArgs e)
+        {
+            screenZoomOrigin = e.GetPosition(parent.mainCanvas);
+            zoomOrigin = parent.ToZoomboxSpace(screenZoomOrigin);
+            initZoomPower = parent.ZoomPowerClamped;
+            initSpaceOriginPos = parent.SpaceOriginPos;
+            parent.mainCanvas.CaptureMouse();
+        }
+
+        public void Update(MouseEventArgs e)
+        {
+            var curScreenPos = e.GetPosition(parent.mainCanvas);
+            double deltaX = screenZoomOrigin.X - curScreenPos.X;
+            double deltaPower = deltaX / 10.0;
+            parent.ZoomPowerClamped = initZoomPower - deltaPower;
+
+            parent.SpaceOriginPos = initSpaceOriginPos;
+            var shiftedOriginPos = parent.ToScreenSpace(zoomOrigin);
+            var deltaOriginPos = shiftedOriginPos - screenZoomOrigin;
+            parent.SpaceOriginPos = initSpaceOriginPos - deltaOriginPos;
+        }
+
+        public void Terminate()
+        {
+            parent.mainCanvas.ReleaseMouseCapture();
+        }
+    }
+}

+ 20 - 0
src/PixiEditor.Zoombox/Zoombox.xaml

@@ -0,0 +1,20 @@
+<ContentControl x:Class="PixiEditor.Zoombox.Zoombox"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
+             xmlns:local="clr-namespace:PixiEditor.Zoombox"
+             mc:Ignorable="d"
+             x:Name="uc"
+             d:DesignHeight="450" d:DesignWidth="800">
+    <Canvas MouseDown="OnMouseDown" MouseUp="OnMouseUp" MouseMove="OnMouseMove" MouseWheel="OnScroll" ClipToBounds="True"
+            IsManipulationEnabled="{Binding UseTouchGestures, ElementName=uc}" ManipulationDelta="OnManipulationDelta"
+            x:Name="mainCanvas" Background="Transparent">
+        <Grid x:Name="mainGrid" SizeChanged="RecalculateMinZoomLevel">
+            <Grid.LayoutTransform>
+                <ScaleTransform x:Name="scaleTransform"/>
+            </Grid.LayoutTransform>
+            <ContentPresenter Content="{Binding AdditionalContent, ElementName=uc}"/>
+        </Grid>
+    </Canvas>
+</ContentControl>

+ 308 - 0
src/PixiEditor.Zoombox/Zoombox.xaml.cs

@@ -0,0 +1,308 @@
+using System;
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Markup;
+
+namespace PixiEditor.Zoombox
+{
+    [ContentProperty(nameof(AdditionalContent))]
+    public partial class Zoombox : ContentControl, INotifyPropertyChanged
+    {
+        public static readonly DependencyProperty AdditionalContentProperty =
+            DependencyProperty.Register(nameof(AdditionalContent), typeof(object), typeof(Zoombox),
+              new PropertyMetadata(null));
+
+        public static readonly DependencyProperty ZoomModeProperty =
+            DependencyProperty.Register(nameof(ZoomMode), typeof(ZoomboxMode), typeof(Zoombox),
+              new PropertyMetadata(ZoomboxMode.Normal, ZoomModeChanged));
+
+        public static readonly DependencyProperty ZoomOutOnClickProperty =
+            DependencyProperty.Register(nameof(ZoomOutOnClick), typeof(bool), typeof(Zoombox),
+              new PropertyMetadata(false));
+
+        public static readonly DependencyProperty UseTouchGesturesProperty =
+            DependencyProperty.Register(nameof(UseTouchGestures), typeof(bool), typeof(Zoombox));
+
+        public static readonly RoutedEvent ViewportMovedEvent = EventManager.RegisterRoutedEvent(
+            nameof(ViewportMoved), RoutingStrategy.Bubble, typeof(EventHandler<ViewportRoutedEventArgs>), typeof(Zoombox));
+
+        private const double zoomFactor = 1.09050773267; //2^(1/8)
+        private const double maxZoom = 50;
+        private double minZoom = -28;
+
+        private double[] roundZoomValues = new double[] { .01, .02, .03, .04, .05, .06, .07, .08, .1, .13, .17, .2, .25, .33, .5, .67, 1, 1.5, 2, 3, 4, 5, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64 };
+        public object? AdditionalContent
+        {
+            get => GetValue(AdditionalContentProperty);
+            set => SetValue(AdditionalContentProperty, value);
+        }
+        public ZoomboxMode ZoomMode
+        {
+            get => (ZoomboxMode)GetValue(ZoomModeProperty);
+            set => SetValue(ZoomModeProperty, value);
+        }
+
+        public bool ZoomOutOnClick
+        {
+            get => (bool)GetValue(ZoomOutOnClickProperty);
+            set => SetValue(ZoomOutOnClickProperty, value);
+        }
+
+        public bool UseTouchGestures
+        {
+            get => (bool)GetValue(UseTouchGesturesProperty);
+            set => SetValue(UseTouchGesturesProperty, value);
+        }
+
+        public event EventHandler<ViewportRoutedEventArgs> ViewportMoved
+        {
+            add => AddHandler(ViewportMovedEvent, value);
+            remove => RemoveHandler(ViewportMovedEvent, value);
+        }
+
+        public double Zoom => Math.Pow(zoomFactor, zoomPower);
+
+        private Point spaceOriginPos;
+        internal Point SpaceOriginPos
+        {
+            get => spaceOriginPos;
+            set
+            {
+                spaceOriginPos = value;
+                Canvas.SetLeft(mainGrid, spaceOriginPos.X);
+                Canvas.SetTop(mainGrid, spaceOriginPos.Y);
+                RaiseViewportEvent();
+            }
+        }
+
+        private double zoomPower;
+        internal double ZoomPowerClamped
+        {
+            get => zoomPower;
+            set
+            {
+                value = Math.Clamp(value, minZoom, maxZoom);
+                if (value == zoomPower)
+                    return;
+                zoomPower = value;
+                var mult = Zoom;
+                scaleTransform.ScaleX = mult;
+                scaleTransform.ScaleY = mult;
+                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Zoom)));
+                RaiseViewportEvent();
+            }
+        }
+        private double ZoomPowerTopCapped
+        {
+            get => zoomPower;
+            set
+            {
+                if (value > maxZoom)
+                    value = maxZoom;
+                if (value == zoomPower)
+                    return;
+                zoomPower = value;
+                var mult = Zoom;
+                scaleTransform.ScaleX = mult;
+                scaleTransform.ScaleY = mult;
+                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Zoom)));
+                RaiseViewportEvent();
+            }
+        }
+
+        private IDragOperation? activeDragOperation = null;
+        private MouseButtonEventArgs? activeMouseDownEventArgs = null;
+        private Point activeMouseDownPos;
+
+        public event PropertyChangedEventHandler? PropertyChanged;
+
+        private static void ZoomModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+        {
+            Zoombox sender = (Zoombox)d;
+            sender.activeDragOperation?.Terminate();
+            sender.activeDragOperation = null;
+            sender.activeMouseDownEventArgs = null;
+        }
+
+        public Zoombox()
+        {
+            InitializeComponent();
+        }
+
+        private void RaiseViewportEvent()
+        {
+            Point center = ToZoomboxSpace(new(mainCanvas.ActualWidth / 2, mainCanvas.ActualHeight / 2));
+            Point topLeft = ToZoomboxSpace(new(0, 0));
+            Point bottomRight = ToZoomboxSpace(new(mainCanvas.ActualWidth, mainCanvas.ActualHeight));
+
+            RaiseEvent(new ViewportRoutedEventArgs(
+                ViewportMovedEvent,
+                new(center.X, center.Y),
+                new(bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y),
+                new(mainCanvas.ActualWidth, mainCanvas.ActualHeight),
+                0));
+        }
+
+        public void CenterContent() => CenterContent(new Size(mainGrid.ActualWidth, mainGrid.ActualHeight));
+
+        public void CenterContent(Size newSize)
+        {
+            const double marginFactor = 1.1;
+            double scaleFactor = Math.Max(
+                newSize.Width * marginFactor / mainCanvas.ActualWidth,
+                newSize.Height * marginFactor / mainCanvas.ActualHeight);
+            ZoomPowerTopCapped = -Math.Log(scaleFactor, zoomFactor);
+            SpaceOriginPos = new Point(
+                mainCanvas.ActualWidth / 2 - newSize.Width * Zoom / 2,
+                mainCanvas.ActualHeight / 2 - newSize.Height * Zoom / 2);
+        }
+
+        public void ZoomIntoCenter(double delta, bool round)
+        {
+            ZoomInto(new Point(mainCanvas.ActualWidth / 2, mainCanvas.ActualHeight / 2), delta, round);
+        }
+
+        public void ZoomInto(Point mousePos, double delta, bool round = false)
+        {
+            if (delta == 0)
+                return;
+            var oldZoomboxMousePos = ToZoomboxSpace(mousePos);
+
+            if (round)
+            {
+                int curIndex = GetClosestRoundZoomValueIndex(Zoom);
+                if (curIndex == 0 && delta < 0 || curIndex == roundZoomValues.Length - 1 && delta > 0)
+                    return;
+                int nextIndex = delta < 0 ? curIndex - 1 : curIndex + 1;
+                double newZoom = roundZoomValues[nextIndex];
+                ZoomPowerClamped = Math.Log(newZoom, zoomFactor);
+            }
+            else
+            {
+                ZoomPowerClamped += delta;
+            }
+
+            if (Math.Abs(ZoomPowerClamped) < 1) ZoomPowerClamped = 0;
+
+            var shiftedMousePos = ToScreenSpace(oldZoomboxMousePos);
+            var deltaMousePos = mousePos - shiftedMousePos;
+            SpaceOriginPos = SpaceOriginPos + deltaMousePos;
+        }
+
+        private int GetClosestRoundZoomValueIndex(double value)
+        {
+            int index = -1;
+            double delta = double.MaxValue;
+            for (int i = 0; i < roundZoomValues.Length; i++)
+            {
+                double curDelta = Math.Abs(roundZoomValues[i] - value);
+                if (curDelta < delta)
+                {
+                    delta = curDelta;
+                    index = i;
+                }
+            }
+            return index;
+        }
+
+        private void RecalculateMinZoomLevel(object sender, SizeChangedEventArgs args)
+        {
+            double fraction = Math.Max(
+                mainCanvas.ActualWidth / mainGrid.ActualWidth,
+                mainCanvas.ActualHeight / mainGrid.ActualHeight);
+            minZoom = Math.Min(0, Math.Log(fraction / 8, zoomFactor));
+        }
+
+        internal Point ToScreenSpace(Point p)
+        {
+            double zoom = Zoom;
+            p.X *= zoom;
+            p.Y *= zoom;
+            p += (Vector)SpaceOriginPos;
+            return p;
+        }
+
+        internal Point ToZoomboxSpace(Point mousePos)
+        {
+            double zoom = Zoom;
+            mousePos -= (Vector)SpaceOriginPos;
+            mousePos.X /= zoom;
+            mousePos.Y /= zoom;
+            return mousePos;
+        }
+
+        private void OnMouseDown(object sender, MouseButtonEventArgs e)
+        {
+            if (e.ChangedButton == MouseButton.Right)
+                return;
+            activeMouseDownEventArgs = e;
+            activeMouseDownPos = e.GetPosition(mainCanvas);
+            Keyboard.Focus(this);
+        }
+
+        private void InitiateDrag(MouseButtonEventArgs e)
+        {
+            if (ZoomMode == ZoomboxMode.Normal)
+                return;
+
+            activeDragOperation?.Terminate();
+
+            if (ZoomMode == ZoomboxMode.Move)
+                activeDragOperation = new MoveDragOperation(this);
+            else if (ZoomMode == ZoomboxMode.Zoom)
+                activeDragOperation = new ZoomDragOperation(this);
+            else
+                throw new InvalidOperationException("Unknown zoombox mode");
+
+            activeDragOperation.Start(e);
+        }
+
+        private void OnMouseUp(object sender, MouseButtonEventArgs e)
+        {
+            if (e.ChangedButton == MouseButton.Right)
+                return;
+            if (activeDragOperation is not null)
+            {
+                activeDragOperation.Terminate();
+                activeDragOperation = null;
+            }
+            else
+            {
+                if (ZoomMode == ZoomboxMode.Zoom && e.ChangedButton == MouseButton.Left)
+                    ZoomInto(e.GetPosition(mainCanvas), ZoomOutOnClick ? -1 : 1, true);
+            }
+            activeMouseDownEventArgs = null;
+        }
+
+        private void OnMouseMove(object sender, MouseEventArgs e)
+        {
+            if (activeDragOperation is null && activeMouseDownEventArgs is not null)
+            {
+                var cur = e.GetPosition(mainCanvas);
+
+                if (Math.Abs(cur.X - activeMouseDownPos.X) > 3)
+                    InitiateDrag(activeMouseDownEventArgs);
+            }
+            activeDragOperation?.Update(e);
+        }
+
+        private void OnScroll(object sender, MouseWheelEventArgs e)
+        {
+            for (int i = 0; i < Math.Abs(e.Delta / 100); i++)
+            {
+                ZoomInto(e.GetPosition(mainCanvas), e.Delta / 100, true);
+            }
+        }
+
+        private void OnManipulationDelta(object sender, ManipulationDeltaEventArgs e)
+        {
+            if (e.Handled = UseTouchGestures)
+            {
+                ZoomInto(e.ManipulationOrigin, e.DeltaManipulation.Expansion.X / 5.0);
+                SpaceOriginPos += e.DeltaManipulation.Translation;
+            }
+        }
+    }
+}

+ 9 - 0
src/PixiEditor.Zoombox/ZoomboxMode.cs

@@ -0,0 +1,9 @@
+namespace PixiEditor.Zoombox
+{
+    public enum ZoomboxMode
+    {
+        Normal,
+        Move,
+        Zoom
+    }
+}

+ 7 - 1
src/PixiEditorPrototype.sln

@@ -5,7 +5,7 @@ VisualStudioVersion = 17.0.31912.275
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PixiEditorPrototype", "PixiEditorPrototype\PixiEditorPrototype.csproj", "{64D7EBA9-A3D0-4832-ACB7-3C519BD23755}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChangeableDocument", "ChangeableDocument\ChangeableDocument.csproj", "{181C9914-75B5-4BEB-AA16-F29B42A401EE}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PixiEditor.ChangeableDocument", "PixiEditor.ChangeableDocument\PixiEditor.ChangeableDocument.csproj", "{181C9914-75B5-4BEB-AA16-F29B42A401EE}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChunkyImageLib", "ChunkyImageLib\ChunkyImageLib.csproj", "{EFA4866B-F03E-4F6F-A7B8-1CA6467D5D17}"
 EndProject
@@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChunkyImageLibTest", "Chunk
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChunkyImageLibVis", "ChunkyImageLibVis\ChunkyImageLibVis.csproj", "{3B0A0186-8AC0-4B1D-8587-4CE4E0E12567}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.Zoombox", "PixiEditor.Zoombox\PixiEditor.Zoombox.csproj", "{232E58B6-8080-4725-8541-98BFCFE23A1C}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -39,6 +41,10 @@ Global
 		{3B0A0186-8AC0-4B1D-8587-4CE4E0E12567}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{3B0A0186-8AC0-4B1D-8587-4CE4E0E12567}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{3B0A0186-8AC0-4B1D-8587-4CE4E0E12567}.Release|Any CPU.Build.0 = Release|Any CPU
+		{232E58B6-8080-4725-8541-98BFCFE23A1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{232E58B6-8080-4725-8541-98BFCFE23A1C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{232E58B6-8080-4725-8541-98BFCFE23A1C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{232E58B6-8080-4725-8541-98BFCFE23A1C}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 2 - 2
src/PixiEditorPrototype/App.xaml

@@ -2,8 +2,8 @@
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:local="clr-namespace:PixiEditorPrototype"
-             StartupUri="MainWindow.xaml">
+             StartupUri="Views/MainWindow.xaml">
     <Application.Resources>
-         
+
     </Application.Resources>
 </Application>

+ 1 - 1
src/PixiEditorPrototype/Behaviors/SliderUpdateBehavior.cs

@@ -6,7 +6,7 @@ using System.Windows.Input;
 
 namespace PixiEditorPrototype.Behaviors
 {
-    class SliderUpdateBehavior : Behavior<Slider>
+    internal class SliderUpdateBehavior : Behavior<Slider>
     {
         public static DependencyProperty DragValueChangedProperty = DependencyProperty.Register(nameof(DragValueChanged), typeof(ICommand), typeof(SliderUpdateBehavior));
         public ICommand? DragValueChanged

+ 0 - 13
src/PixiEditorPrototype/MainWindow.xaml

@@ -1,13 +0,0 @@
-<Window x:Class="PixiEditorPrototype.MainWindow" x:ClassModifier="internal"
-        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
-        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-        xmlns:local="clr-namespace:PixiEditorPrototype"
-        xmlns:views="clr-namespace:PixiEditorPrototype.Views"
-        mc:Ignorable="d"
-        Title="MainWindow" Height="576" Width="1024">
-    <DockPanel>
-        <views:DocumentView DataContext="{Binding Document}"></views:DocumentView>
-    </DockPanel>
-</Window>

+ 0 - 18
src/PixiEditorPrototype/MainWindow.xaml.cs

@@ -1,18 +0,0 @@
-using PixiEditorPrototype.ViewModels;
-using System.Windows;
-
-namespace PixiEditorPrototype
-{
-    /// <summary>
-    /// Interaction logic for MainWindow.xaml
-    /// </summary>
-    internal partial class MainWindow : Window
-    {
-        public DocumentViewModel Document { get; set; } = new();
-        public MainWindow()
-        {
-            InitializeComponent();
-            DataContext = this;
-        }
-    }
-}

+ 12 - 14
src/PixiEditorPrototype/Models/ActionAccumulator.cs

@@ -1,7 +1,6 @@
-using ChangeableDocument;
-using ChangeableDocument.Actions;
-using ChangeableDocument.ChangeInfos;
-using ChunkyImageLib.DataHolders;
+using ChunkyImageLib.DataHolders;
+using PixiEditor.ChangeableDocument.Actions;
+using PixiEditor.ChangeableDocument.ChangeInfos;
 using PixiEditorPrototype.Models.Rendering;
 using PixiEditorPrototype.Models.Rendering.RenderInfos;
 using PixiEditorPrototype.ViewModels;
@@ -17,18 +16,17 @@ namespace PixiEditorPrototype.Models
         private bool executing = false;
 
         private List<IAction> queuedActions = new();
-        private DocumentChangeTracker tracker;
         private DocumentViewModel document;
-        private DocumentUpdater documentUpdater;
+        private DocumentHelpers helpers;
+
         private WriteableBitmapUpdater renderer;
 
-        public ActionAccumulator(DocumentChangeTracker tracker, DocumentUpdater updater, DocumentViewModel document)
+        public ActionAccumulator(DocumentViewModel doc, DocumentHelpers helpers)
         {
-            this.tracker = tracker;
-            this.documentUpdater = updater;
-            this.document = document;
+            this.document = doc;
+            this.helpers = helpers;
 
-            renderer = new(tracker);
+            renderer = new(helpers);
         }
 
         public void AddAction(IAction action)
@@ -48,10 +46,10 @@ namespace PixiEditorPrototype.Models
                 var toExecute = queuedActions;
                 queuedActions = new List<IAction>();
 
-                var result = await tracker.ProcessActions(toExecute);
+                var result = await helpers.Tracker.ProcessActions(toExecute);
                 foreach (IChangeInfo? info in result)
                 {
-                    documentUpdater.ApplyChangeFromChangeInfo(info);
+                    helpers.Updater.ApplyChangeFromChangeInfo(info);
                 }
 
                 var (bitmap, surface) = GetCorrespondingBitmap(document.RenderResolution);
@@ -64,7 +62,7 @@ namespace PixiEditorPrototype.Models
                 AddDirtyRects(bitmap, renderResult);
 
                 bitmap.Unlock();
-                document.View?.ForceRefreshFinalImage();
+                document.ForceRefreshView();
             }
 
             executing = false;

+ 22 - 0
src/PixiEditorPrototype/Models/DocumentHelpers.cs

@@ -0,0 +1,22 @@
+using ChangeableDocument;
+using PixiEditorPrototype.ViewModels;
+
+namespace PixiEditorPrototype.Models
+{
+    internal class DocumentHelpers
+    {
+        public DocumentHelpers(DocumentViewModel doc)
+        {
+            Tracker = new DocumentChangeTracker();
+            StructureHelper = new DocumentStructureHelper(doc, this);
+            Updater = new DocumentUpdater(doc, this);
+            ActionAccumulator = new ActionAccumulator(doc, this);
+            State = new DocumentState();
+        }
+        public ActionAccumulator ActionAccumulator { get; }
+        public DocumentChangeTracker Tracker { get; }
+        public DocumentStructureHelper StructureHelper { get; }
+        public DocumentUpdater Updater { get; }
+        public DocumentState State { get; }
+    }
+}

+ 12 - 0
src/PixiEditorPrototype/Models/DocumentState.cs

@@ -0,0 +1,12 @@
+using ChunkyImageLib.DataHolders;
+
+namespace PixiEditorPrototype.Models
+{
+    internal class DocumentState
+    {
+        public Vector2d ViewportCenter { get; set; } = new(32, 32);
+        public Vector2d ViewportSize { get; set; } = new(64, 64);
+        public Vector2d ViewportRealSize { get; set; } = new(double.MaxValue, double.MaxValue);
+        public double ViewportAngle { get; set; } = 0;
+    }
+}

+ 11 - 9
src/PixiEditorPrototype/Models/DocumentStructureHelper.cs

@@ -1,5 +1,5 @@
-using ChangeableDocument;
-using ChangeableDocument.Actions.Structure;
+using PixiEditor.ChangeableDocument;
+using PixiEditor.ChangeableDocument.Actions.Structure;
 using PixiEditorPrototype.ViewModels;
 using System;
 using System.Collections.Generic;
@@ -9,9 +9,11 @@ namespace PixiEditorPrototype.Models
     internal class DocumentStructureHelper
     {
         private DocumentViewModel doc;
-        public DocumentStructureHelper(DocumentViewModel doc)
+        private DocumentHelpers helpers;
+        public DocumentStructureHelper(DocumentViewModel doc, DocumentHelpers helpers)
         {
             this.doc = doc;
+            this.helpers = helpers;
         }
 
         public void CreateNewStructureMember(StructureMemberType type)
@@ -19,13 +21,13 @@ namespace PixiEditorPrototype.Models
             if (doc.SelectedStructureMember is null)
             {
                 //put member on top
-                doc.ActionAccumulator.AddAction(new CreateStructureMember_Action(doc.StructureRoot.GuidValue, Guid.NewGuid(), doc.StructureRoot.Children.Count, type));
+                helpers.ActionAccumulator.AddAction(new CreateStructureMember_Action(doc.StructureRoot.GuidValue, Guid.NewGuid(), doc.StructureRoot.Children.Count, type));
                 return;
             }
             if (doc.SelectedStructureMember is FolderViewModel folder)
             {
                 //put member inside folder on top
-                doc.ActionAccumulator.AddAction(new CreateStructureMember_Action(folder.GuidValue, Guid.NewGuid(), folder.Children.Count, type));
+                helpers.ActionAccumulator.AddAction(new CreateStructureMember_Action(folder.GuidValue, Guid.NewGuid(), folder.Children.Count, type));
                 return;
             }
             if (doc.SelectedStructureMember is LayerViewModel layer)
@@ -35,7 +37,7 @@ namespace PixiEditorPrototype.Models
                 if (path.Count < 2)
                     throw new InvalidOperationException("Couldn't find a path to the selected member");
                 var parent = (FolderViewModel)path[1];
-                doc.ActionAccumulator.AddAction(new CreateStructureMember_Action(parent.GuidValue, Guid.NewGuid(), parent.Children.IndexOf(layer) + 1, type));
+                helpers.ActionAccumulator.AddAction(new CreateStructureMember_Action(parent.GuidValue, Guid.NewGuid(), parent.Children.IndexOf(layer) + 1, type));
                 return;
             }
             throw new ArgumentException("Unknown member type: " + type.ToString());
@@ -98,19 +100,19 @@ namespace PixiEditorPrototype.Models
                 int curIndex = doc.StructureRoot.Children.IndexOf(path[0]);
                 if (curIndex == 0 && toSmallerIndex || curIndex == doc.StructureRoot.Children.Count - 1 && !toSmallerIndex)
                     return;
-                doc.ActionAccumulator.AddAction(new MoveStructureMember_Action(guid, doc.StructureRoot.GuidValue, toSmallerIndex ? curIndex - 1 : curIndex + 1));
+                helpers.ActionAccumulator.AddAction(new MoveStructureMember_Action(guid, doc.StructureRoot.GuidValue, toSmallerIndex ? curIndex - 1 : curIndex + 1));
                 return;
             }
             var folder = (FolderViewModel)path[1];
             int index = folder.Children.IndexOf(path[0]);
             if (toSmallerIndex && index > 0 || !toSmallerIndex && index < folder.Children.Count - 1)
             {
-                doc.ActionAccumulator.AddAction(new MoveStructureMember_Action(guid, path[1].GuidValue, toSmallerIndex ? index - 1 : index + 1));
+                helpers.ActionAccumulator.AddAction(new MoveStructureMember_Action(guid, path[1].GuidValue, toSmallerIndex ? index - 1 : index + 1));
             }
             else
             {
                 int parentIndex = ((FolderViewModel)path[2]).Children.IndexOf(folder);
-                doc.ActionAccumulator.AddAction(new MoveStructureMember_Action(guid, path[2].GuidValue, toSmallerIndex ? parentIndex : parentIndex + 1));
+                helpers.ActionAccumulator.AddAction(new MoveStructureMember_Action(guid, path[2].GuidValue, toSmallerIndex ? parentIndex : parentIndex + 1));
             }
         }
     }

+ 40 - 24
src/PixiEditorPrototype/Models/DocumentUpdater.cs

@@ -1,6 +1,6 @@
-using ChangeableDocument.Changeables.Interfaces;
-using ChangeableDocument.ChangeInfos;
-using ChunkyImageLib.DataHolders;
+using ChunkyImageLib.DataHolders;
+using PixiEditor.ChangeableDocument.Changeables.Interfaces;
+using PixiEditor.ChangeableDocument.ChangeInfos;
 using PixiEditorPrototype.ViewModels;
 using SkiaSharp;
 using System;
@@ -12,9 +12,11 @@ namespace PixiEditorPrototype.Models
     internal class DocumentUpdater
     {
         private DocumentViewModel doc;
-        public DocumentUpdater(DocumentViewModel doc)
+        private DocumentHelpers helper;
+        public DocumentUpdater(DocumentViewModel doc, DocumentHelpers helper)
         {
             this.doc = doc;
+            this.helper = helper;
         }
 
         public void ApplyChangeFromChangeInfo(IChangeInfo? arbitraryInfo)
@@ -53,8 +55,17 @@ namespace PixiEditorPrototype.Models
 
         private void ProcessMoveViewport(MoveViewport_PassthroughAction info)
         {
-            doc.ChosenResolution = info.Resolution;
-            doc.RaisePropertyChanged(nameof(doc.RenderBitmap));
+            var oldResolution = doc.RenderResolution;
+
+            helper.State.ViewportCenter = info.Center;
+            helper.State.ViewportSize = info.Size;
+            helper.State.ViewportAngle = info.Angle;
+            helper.State.ViewportRealSize = info.RealSize;
+
+            var newResolution = doc.RenderResolution;
+
+            if (oldResolution != newResolution)
+                doc.RaisePropertyChanged(nameof(doc.RenderBitmap));
         }
 
         private void ProcessSize(Size_ChangeInfo info)
@@ -72,27 +83,32 @@ namespace PixiEditorPrototype.Models
             doc.BitmapQuarter = null;
             doc.BitmapEighth = null;
 
-            doc.BitmapFull = CreateBitmap(doc.Tracker.Document.Size);
+            doc.BitmapFull = CreateBitmap(helper.Tracker.Document.Size);
             doc.SurfaceFull = CreateSKSurface(doc.BitmapFull);
 
-            if (doc.Tracker.Document.Size.X > 512 && doc.Tracker.Document.Size.Y > 512)
+            if (helper.Tracker.Document.Size.X > 512 && helper.Tracker.Document.Size.Y > 512)
             {
-                doc.BitmapHalf = CreateBitmap(doc.Tracker.Document.Size / 2);
+                doc.BitmapHalf = CreateBitmap(helper.Tracker.Document.Size / 2);
                 doc.SurfaceHalf = CreateSKSurface(doc.BitmapHalf);
             }
 
-            if (doc.Tracker.Document.Size.X > 1024 && doc.Tracker.Document.Size.Y > 1024)
+            if (helper.Tracker.Document.Size.X > 1024 && helper.Tracker.Document.Size.Y > 1024)
             {
-                doc.BitmapQuarter = CreateBitmap(doc.Tracker.Document.Size / 4);
+                doc.BitmapQuarter = CreateBitmap(helper.Tracker.Document.Size / 4);
                 doc.SurfaceQuarter = CreateSKSurface(doc.BitmapQuarter);
             }
 
-            if (doc.Tracker.Document.Size.X > 2048 && doc.Tracker.Document.Size.Y > 2048)
+            if (helper.Tracker.Document.Size.X > 2048 && helper.Tracker.Document.Size.Y > 2048)
             {
-                doc.BitmapEighth = CreateBitmap(doc.Tracker.Document.Size / 8);
+                doc.BitmapEighth = CreateBitmap(helper.Tracker.Document.Size / 8);
                 doc.SurfaceEighth = CreateSKSurface(doc.BitmapEighth);
             }
 
+            doc.RaisePropertyChanged(nameof(doc.BitmapFull));
+            doc.RaisePropertyChanged(nameof(doc.BitmapHalf));
+            doc.RaisePropertyChanged(nameof(doc.BitmapQuarter));
+            doc.RaisePropertyChanged(nameof(doc.BitmapEighth));
+
             doc.RaisePropertyChanged(nameof(doc.RenderBitmap));
         }
 
@@ -111,15 +127,15 @@ namespace PixiEditorPrototype.Models
 
         private void ProcessCreateStructureMember(CreateStructureMember_ChangeInfo info)
         {
-            var (member, parentFolder) = doc.Tracker.Document.FindChildAndParentOrThrow(info.GuidValue);
-            var parentFolderVM = (FolderViewModel)doc.StructureHelper.FindOrThrow(parentFolder.GuidValue);
+            var (member, parentFolder) = helper.Tracker.Document.FindChildAndParentOrThrow(info.GuidValue);
+            var parentFolderVM = (FolderViewModel)helper.StructureHelper.FindOrThrow(parentFolder.GuidValue);
 
             int index = parentFolder.ReadOnlyChildren.IndexOf(member);
 
             StructureMemberViewModel memberVM = member switch
             {
-                IReadOnlyLayer layer => new LayerViewModel(doc, layer),
-                IReadOnlyFolder folder => new FolderViewModel(doc, folder),
+                IReadOnlyLayer layer => new LayerViewModel(doc, helper, layer),
+                IReadOnlyFolder folder => new FolderViewModel(doc, helper, folder),
                 _ => throw new InvalidOperationException("Unsupposed member type")
             };
 
@@ -136,35 +152,35 @@ namespace PixiEditorPrototype.Models
 
         private void ProcessDeleteStructureMember(DeleteStructureMember_ChangeInfo info)
         {
-            var (memberVM, folderVM) = doc.StructureHelper.FindChildAndParentOrThrow(info.GuidValue);
+            var (memberVM, folderVM) = helper.StructureHelper.FindChildAndParentOrThrow(info.GuidValue);
             folderVM.Children.Remove(memberVM);
         }
 
         private void ProcessUpdateStructureMemberIsVisible(StructureMemberIsVisible_ChangeInfo info)
         {
-            var memberVM = doc.StructureHelper.FindOrThrow(info.GuidValue);
+            var memberVM = helper.StructureHelper.FindOrThrow(info.GuidValue);
             memberVM.RaisePropertyChanged(nameof(memberVM.IsVisible));
         }
 
         private void ProcessUpdateStructureMemberName(StructureMemberName_ChangeInfo info)
         {
-            var memberVM = doc.StructureHelper.FindOrThrow(info.GuidValue);
+            var memberVM = helper.StructureHelper.FindOrThrow(info.GuidValue);
             memberVM.RaisePropertyChanged(nameof(memberVM.Name));
         }
 
         private void ProcessUpdateStructureMemberOpacity(StructureMemberOpacity_ChangeInfo info)
         {
-            var memberVM = doc.StructureHelper.FindOrThrow(info.GuidValue);
+            var memberVM = helper.StructureHelper.FindOrThrow(info.GuidValue);
             memberVM.RaisePropertyChanged(nameof(memberVM.Opacity));
         }
 
         private void ProcessMoveStructureMember(MoveStructureMember_ChangeInfo info)
         {
-            var (memberVM, curFolderVM) = doc.StructureHelper.FindChildAndParentOrThrow(info.GuidValue);
-            var (member, targetFolder) = doc.Tracker.Document.FindChildAndParentOrThrow(info.GuidValue);
+            var (memberVM, curFolderVM) = helper.StructureHelper.FindChildAndParentOrThrow(info.GuidValue);
+            var (member, targetFolder) = helper.Tracker.Document.FindChildAndParentOrThrow(info.GuidValue);
 
             int index = targetFolder.ReadOnlyChildren.IndexOf(member);
-            var targetFolderVM = (FolderViewModel)doc.StructureHelper.FindOrThrow(targetFolder.GuidValue);
+            var targetFolderVM = (FolderViewModel)helper.StructureHelper.FindOrThrow(targetFolder.GuidValue);
 
             curFolderVM.Children.Remove(memberVM);
             targetFolderVM.Children.Insert(index, memberVM);

+ 12 - 9
src/PixiEditorPrototype/Models/MoveViewport_PassthroughAction.cs

@@ -1,19 +1,22 @@
-using ChangeableDocument.Actions;
-using ChangeableDocument.ChangeInfos;
-using ChunkyImageLib.DataHolders;
-using SkiaSharp;
+using ChunkyImageLib.DataHolders;
+using PixiEditor.ChangeableDocument.Actions;
+using PixiEditor.ChangeableDocument.ChangeInfos;
 
 namespace PixiEditorPrototype.Models
 {
     internal record class MoveViewport_PassthroughAction : IAction, IChangeInfo
     {
-        public MoveViewport_PassthroughAction(SKRect viewport, ChunkResolution resolution)
+        public MoveViewport_PassthroughAction(Vector2d center, Vector2d size, double angle, Vector2d realSize)
         {
-            Viewport = viewport;
-            Resolution = resolution;
+            Center = center;
+            Size = size;
+            Angle = angle;
+            RealSize = realSize;
         }
 
-        public SKRect Viewport { get; }
-        public ChunkResolution Resolution { get; }
+        public Vector2d Center { get; }
+        public Vector2d Size { get; }
+        public Vector2d RealSize { get; }
+        public double Angle { get; }
     }
 }

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

@@ -1,28 +1,26 @@
-using ChangeableDocument;
-using ChangeableDocument.Changeables.Interfaces;
-using ChangeableDocument.ChangeInfos;
-using ChangeableDocument.Rendering;
-using ChunkyImageLib;
+using ChunkyImageLib;
 using ChunkyImageLib.DataHolders;
+using ChunkyImageLib.Operations;
+using PixiEditor.ChangeableDocument.Changeables.Interfaces;
+using PixiEditor.ChangeableDocument.ChangeInfos;
+using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditorPrototype.Models.Rendering.RenderInfos;
 using SkiaSharp;
 using System;
 using System.Collections.Generic;
-using System.Linq;
 using System.Threading.Tasks;
 
 namespace PixiEditorPrototype.Models.Rendering
 {
-    public class WriteableBitmapUpdater
+    internal class WriteableBitmapUpdater
     {
-        private DocumentChangeTracker tracker;
+        private DocumentHelpers helpers;
+
         private static SKPaint BlendingPaint = new SKPaint() { BlendMode = SKBlendMode.SrcOver };
         private static SKPaint ReplacingPaint = new SKPaint() { BlendMode = SKBlendMode.Src };
         private static SKPaint SelectionPaint = new SKPaint() { BlendMode = SKBlendMode.SrcOver, Color = new(0xa0FFFFFF) };
         private static SKPaint ClearPaint = new SKPaint() { BlendMode = SKBlendMode.Src, Color = SKColors.Transparent };
 
-        private SKRect lastViewport = SKRect.Create(0, 0, 64, 64);
-
         private Dictionary<ChunkResolution, HashSet<Vector2i>> postponedChunks = new()
         {
             [ChunkResolution.Full] = new(),
@@ -31,9 +29,9 @@ namespace PixiEditorPrototype.Models.Rendering
             [ChunkResolution.Eighth] = new()
         };
 
-        public WriteableBitmapUpdater(DocumentChangeTracker tracker)
+        public WriteableBitmapUpdater(DocumentHelpers helpers)
         {
-            this.tracker = tracker;
+            this.helpers = helpers;
         }
 
         public async Task<List<IRenderInfo>> ProcessChanges(IReadOnlyList<IChangeInfo> changes, SKSurface screenSurface, ChunkResolution resolution)
@@ -59,7 +57,7 @@ namespace PixiEditorPrototype.Models.Rendering
                         affectedChunks.UnionWith(layerImageChunks.Chunks);
                         break;
                     case Selection_ChangeInfo selection:
-                        if (tracker.Document.ReadOnlySelection.ReadOnlyIsEmptyAndInactive)
+                        if (helpers.Tracker.Document.ReadOnlySelection.ReadOnlyIsEmptyAndInactive)
                         {
                             AddAllChunks(affectedChunks);
                         }
@@ -77,21 +75,21 @@ namespace PixiEditorPrototype.Models.Rendering
                         AddAllChunks(affectedChunks);
                         break;
                     case StructureMemberOpacity_ChangeInfo opacityChangeInfo:
-                        var memberWithOpacity = tracker.Document.FindMemberOrThrow(opacityChangeInfo.GuidValue);
+                        var memberWithOpacity = helpers.Tracker.Document.FindMemberOrThrow(opacityChangeInfo.GuidValue);
                         if (memberWithOpacity is IReadOnlyLayer layerWithOpacity)
                             affectedChunks.UnionWith(layerWithOpacity.ReadOnlyLayerImage.FindAllChunks());
                         else
                             AddAllChunks(affectedChunks);
                         break;
                     case StructureMemberIsVisible_ChangeInfo visibilityChangeInfo:
-                        var memberWithVisibility = tracker.Document.FindMemberOrThrow(visibilityChangeInfo.GuidValue);
+                        var memberWithVisibility = helpers.Tracker.Document.FindMemberOrThrow(visibilityChangeInfo.GuidValue);
                         if (memberWithVisibility is IReadOnlyLayer layerWithVisibility)
                             affectedChunks.UnionWith(layerWithVisibility.ReadOnlyLayerImage.FindAllChunks());
                         else
                             AddAllChunks(affectedChunks);
                         break;
                     case MoveViewport_PassthroughAction moveViewportInfo:
-                        lastViewport = moveViewportInfo.Viewport;
+
                         break;
                 }
             }
@@ -101,21 +99,23 @@ namespace PixiEditorPrototype.Models.Rendering
             postponedChunks[ChunkResolution.Quarter].UnionWith(affectedChunks);
             postponedChunks[ChunkResolution.Eighth].UnionWith(affectedChunks);
 
-            HashSet<Vector2i> visibleChunks = postponedChunks[resolution].Where(pos =>
-            {
-                var rect = SKRect.Create(pos, new(ChunkyImage.ChunkSize, ChunkyImage.ChunkSize));
-                return rect.IntersectsWith(lastViewport);
-            }).ToHashSet();
-            postponedChunks[resolution].ExceptWith(visibleChunks);
+            var chunksOnScreen = OperationHelper.FindChunksTouchingRectangle(
+                helpers.State.ViewportCenter,
+                helpers.State.ViewportSize,
+                helpers.State.ViewportAngle,
+                ChunkResolution.Full.PixelSize());
+
+            chunksOnScreen.IntersectWith(postponedChunks[resolution]);
+            postponedChunks[resolution].ExceptWith(chunksOnScreen);
 
-            return visibleChunks;
+            return chunksOnScreen;
         }
 
         private void AddAllChunks(HashSet<Vector2i> chunks)
         {
             Vector2i size = new(
-                (int)Math.Ceiling(tracker.Document.Size.X / (float)ChunkyImage.ChunkSize),
-                (int)Math.Ceiling(tracker.Document.Size.Y / (float)ChunkyImage.ChunkSize));
+                (int)Math.Ceiling(helpers.Tracker.Document.Size.X / (float)ChunkyImage.ChunkSize),
+                (int)Math.Ceiling(helpers.Tracker.Document.Size.Y / (float)ChunkyImage.ChunkSize));
             for (int i = 0; i < size.X; i++)
             {
                 for (int j = 0; j < size.Y; j++)
@@ -146,13 +146,13 @@ namespace PixiEditorPrototype.Models.Rendering
 
         private void RenderChunk(Vector2i chunkPos, SKSurface screenSurface, ChunkResolution resolution)
         {
-            using Chunk renderedChunk = ChunkRenderer.RenderWholeStructure(chunkPos, resolution, tracker.Document.ReadOnlyStructureRoot);
+            using Chunk renderedChunk = ChunkRenderer.RenderWholeStructure(chunkPos, resolution, helpers.Tracker.Document.ReadOnlyStructureRoot);
 
             screenSurface.Canvas.DrawSurface(renderedChunk.Surface.SkiaSurface, chunkPos.Multiply(renderedChunk.PixelSize), ReplacingPaint);
 
-            if (tracker.Document.ReadOnlySelection.ReadOnlyIsEmptyAndInactive)
+            if (helpers.Tracker.Document.ReadOnlySelection.ReadOnlyIsEmptyAndInactive)
                 return;
-            IReadOnlyChunk? selectionChunk = tracker.Document.ReadOnlySelection.ReadOnlySelectionImage.GetLatestChunk(chunkPos, resolution);
+            IReadOnlyChunk? selectionChunk = helpers.Tracker.Document.ReadOnlySelection.ReadOnlySelectionImage.GetLatestChunk(chunkPos, resolution);
             if (selectionChunk is not null)
                 selectionChunk.DrawOnSurface(screenSurface, chunkPos.Multiply(selectionChunk.PixelSize), SelectionPaint);
         }

+ 1 - 1
src/PixiEditorPrototype/Models/Tool.cs

@@ -1,6 +1,6 @@
 namespace PixiEditorPrototype.Models
 {
-    enum Tool
+    internal enum Tool
     {
         Rectangle,
         Select

+ 2 - 1
src/PixiEditorPrototype/PixiEditorPrototype.csproj

@@ -13,8 +13,9 @@
   </ItemGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\ChangeableDocument\ChangeableDocument.csproj" />
     <ProjectReference Include="..\ChunkyImageLib\ChunkyImageLib.csproj" />
+    <ProjectReference Include="..\PixiEditor.ChangeableDocument\PixiEditor.ChangeableDocument.csproj" />
+    <ProjectReference Include="..\PixiEditor.Zoombox\PixiEditor.Zoombox.csproj" />
   </ItemGroup>
 
 </Project>

+ 87 - 136
src/PixiEditorPrototype/ViewModels/DocumentViewModel.cs

@@ -1,21 +1,20 @@
-using ChangeableDocument;
-using ChangeableDocument.Actions.Document;
-using ChangeableDocument.Actions.Drawing;
-using ChangeableDocument.Actions.Drawing.Rectangle;
-using ChangeableDocument.Actions.Drawing.Selection;
-using ChangeableDocument.Actions.Properties;
-using ChangeableDocument.Actions.Structure;
-using ChangeableDocument.Actions.Undo;
-using ChunkyImageLib.DataHolders;
+using ChunkyImageLib.DataHolders;
+using PixiEditor.ChangeableDocument;
+using PixiEditor.ChangeableDocument.Actions.Document;
+using PixiEditor.ChangeableDocument.Actions.Drawing;
+using PixiEditor.ChangeableDocument.Actions.Drawing.Rectangle;
+using PixiEditor.ChangeableDocument.Actions.Drawing.Selection;
+using PixiEditor.ChangeableDocument.Actions.Properties;
+using PixiEditor.ChangeableDocument.Actions.Structure;
+using PixiEditor.ChangeableDocument.Actions.Undo;
+using PixiEditor.Zoombox;
 using PixiEditorPrototype.Models;
-using PixiEditorPrototype.Views;
 using SkiaSharp;
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Linq;
 using System.Windows;
-using System.Windows.Input;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 
@@ -34,8 +33,6 @@ namespace PixiEditorPrototype.ViewModels
             }
         }
 
-        private Tool activeTool = Tool.Rectangle;
-
         public event PropertyChangedEventHandler? PropertyChanged;
 
         public void RaisePropertyChanged(string name)
@@ -43,12 +40,6 @@ namespace PixiEditorPrototype.ViewModels
             PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
         }
 
-        public ActionAccumulator ActionAccumulator { get; }
-        public DocumentChangeTracker Tracker { get; }
-        public DocumentStructureHelper StructureHelper { get; }
-        public IDocumentView? View { get; set; }
-        private DocumentUpdater Updater { get; }
-
         public FolderViewModel StructureRoot { get; }
         public RelayCommand? UndoCommand { get; }
         public RelayCommand? RedoCommand { get; }
@@ -57,14 +48,10 @@ namespace PixiEditorPrototype.ViewModels
         public RelayCommand? CreateNewFolderCommand { get; }
         public RelayCommand? DeleteStructureMemberCommand { get; }
         public RelayCommand? ChangeSelectedItemCommand { get; }
-        public RelayCommand? ChangeActiveToolCommand { get; }
         public RelayCommand? ResizeCanvasCommand { get; }
         public RelayCommand? CombineCommand { get; }
         public RelayCommand? ClearHistoryCommand { get; }
-
-        public RelayCommand? MouseDownCommand { get; }
-        public RelayCommand? MouseMoveCommand { get; }
-        public RelayCommand? MouseUpCommand { get; }
+        public RelayCommand? MoveViewportCommand { get; }
 
 
         public SKSurface SurfaceFull { get; set; }
@@ -85,18 +72,24 @@ namespace PixiEditorPrototype.ViewModels
         {
             get
             {
-                if (GetCorrespondingBitmap(ChosenResolution) is not null)
-                    return ChosenResolution;
+                var targetRes = GetResolutionForViewport(Helpers.State.ViewportSize, Helpers.State.ViewportRealSize);
+                if (GetCorrespondingBitmap(targetRes) is not null)
+                    return targetRes;
                 return ChunkResolution.Full;
             }
         }
-        public ChunkResolution ChosenResolution { get; set; }
-        public ChunkResolution ResolutionFromView
+
+        public ChunkResolution GetResolutionForViewport(Vector2d size, Vector2d realSize)
         {
-            set
-            {
-                ActionAccumulator.AddAction(new MoveViewport_PassthroughAction(SKRect.Create(0, 0, BitmapFull.PixelWidth, BitmapFull.PixelHeight), value));
-            }
+            Vector2d densityVec = size.Divide(realSize);
+            double density = Math.Min(densityVec.X, densityVec.Y);
+            if (density > 8)
+                return ChunkResolution.Eighth;
+            else if (density > 4)
+                return ChunkResolution.Quarter;
+            else if (density > 2)
+                return ChunkResolution.Half;
+            return ChunkResolution.Full;
         }
 
         public WriteableBitmap? GetCorrespondingBitmap(ChunkResolution resolution)
@@ -111,34 +104,33 @@ namespace PixiEditorPrototype.ViewModels
             };
         }
 
-
-        public Color SelectedColor { get; set; } = Colors.Black;
         public int ResizeWidth { get; set; }
         public int ResizeHeight { get; set; }
 
-        public DocumentViewModel()
+        private DocumentHelpers Helpers { get; }
+        public object MoveViewportEventArgs { get; private set; }
+
+        private ViewModelMain owner;
+
+        public DocumentViewModel(ViewModelMain owner)
         {
-            Tracker = new DocumentChangeTracker();
-            Updater = new DocumentUpdater(this);
-            StructureRoot = new FolderViewModel(this, Tracker.Document.ReadOnlyStructureRoot);
-            ActionAccumulator = new ActionAccumulator(Tracker, Updater, this);
-            StructureHelper = new DocumentStructureHelper(this);
+            this.owner = owner;
+
+            Helpers = new DocumentHelpers(this);
+            StructureRoot = new FolderViewModel(this, Helpers, Helpers.Tracker.Document.ReadOnlyStructureRoot);
+
 
             UndoCommand = new RelayCommand(Undo);
             RedoCommand = new RelayCommand(Redo);
             ClearSelectionCommand = new RelayCommand(ClearSelection);
-            CreateNewLayerCommand = new RelayCommand(_ => StructureHelper.CreateNewStructureMember(StructureMemberType.Layer));
-            CreateNewFolderCommand = new RelayCommand(_ => StructureHelper.CreateNewStructureMember(StructureMemberType.Folder));
+            CreateNewLayerCommand = new RelayCommand(_ => Helpers.StructureHelper.CreateNewStructureMember(StructureMemberType.Layer));
+            CreateNewFolderCommand = new RelayCommand(_ => Helpers.StructureHelper.CreateNewStructureMember(StructureMemberType.Folder));
             DeleteStructureMemberCommand = new RelayCommand(DeleteStructureMember);
             ChangeSelectedItemCommand = new RelayCommand(ChangeSelectedItem);
-            ChangeActiveToolCommand = new RelayCommand(ChangeActiveTool);
             ResizeCanvasCommand = new RelayCommand(ResizeCanvas);
             CombineCommand = new RelayCommand(Combine);
             ClearHistoryCommand = new RelayCommand(ClearHistory);
-
-            MouseDownCommand = new RelayCommand(MouseDown);
-            MouseMoveCommand = new RelayCommand(MouseMove);
-            MouseUpCommand = new RelayCommand(MouseUp);
+            MoveViewportCommand = new RelayCommand(MoveViewport);
 
             SurfaceFull = SKSurface.Create(
                 new SKImageInfo(BitmapFull.PixelWidth, BitmapFull.PixelHeight, SKColorType.Bgra8888, SKAlphaType.Premul, SKColorSpace.CreateSrgb()),
@@ -146,112 +138,70 @@ namespace PixiEditorPrototype.ViewModels
                 BitmapFull.BackBufferStride);
         }
 
-        private bool mouseIsDown = false;
-        private int mouseDownCanvasX = 0;
-        private int mouseDownCanvasY = 0;
-
-        private bool startedDrawingRect = false;
-        private bool startedSelectingRect = false;
-
-        public void MouseDown(object? param)
+        bool startedRectangle = false;
+        public void StartUpdateRectangle(ShapeData data)
         {
-            mouseIsDown = true;
-            var args = (MouseButtonEventArgs)(param!);
-            var source = (System.Windows.Controls.Image)args.Source;
-            var pos = args.GetPosition(source);
-            mouseDownCanvasX = (int)(pos.X / source.Width * BitmapFull.PixelHeight);
-            mouseDownCanvasY = (int)(pos.Y / source.Height * BitmapFull.PixelHeight);
+            if (SelectedStructureMember is null)
+                return;
+            startedRectangle = true;
+            Helpers.ActionAccumulator.AddAction(new DrawRectangle_Action(SelectedStructureMember.GuidValue, data));
         }
 
-        public void MouseMove(object? param)
+        public void EndRectangle()
         {
-            if (!mouseIsDown)
+            if (!startedRectangle)
                 return;
-            var args = (MouseEventArgs)(param!);
-            var source = (System.Windows.Controls.Image)args.Source;
-            var pos = args.GetPosition(source);
-            int curX = (int)(pos.X / source.Width * BitmapFull.PixelHeight);
-            int curY = (int)(pos.Y / source.Height * BitmapFull.PixelHeight);
-
-            ProcessToolMouseMove(curX, curY);
+            startedRectangle = false;
+            Helpers.ActionAccumulator.AddAction(new EndDrawRectangle_Action());
         }
 
-        private void ProcessToolMouseMove(int canvasX, int canvasY)
+        bool startedSelection = false;
+        public void StartUpdateSelection(Vector2i pos, Vector2i size)
         {
-            if (activeTool == Tool.Rectangle)
-            {
-                if (SelectedStructureMember is null)
-                    return;
-                startedDrawingRect = true;
-                ActionAccumulator.AddAction(new DrawRectangle_Action(
-                        SelectedStructureMember.GuidValue,
-                        new ShapeData(
-                            new(mouseDownCanvasX, mouseDownCanvasY),
-                            new(canvasX - mouseDownCanvasX, canvasY - mouseDownCanvasY),
-                            90,
-                            new SKColor(SelectedColor.R, SelectedColor.G, SelectedColor.B, SelectedColor.A),
-                            SKColors.Transparent)
-                    ));
-            }
-            else if (activeTool == Tool.Select)
-            {
-                if (!startedSelectingRect)
-                    ActionAccumulator.AddAction(new ClearSelection_Action());
-                startedSelectingRect = true;
-                ActionAccumulator.AddAction(new SelectRectangle_Action(
-                        new(mouseDownCanvasX, mouseDownCanvasY),
-                        new(canvasX - mouseDownCanvasX, canvasY - mouseDownCanvasY)
-                    ));
-            }
+            if (!startedSelection)
+                Helpers.ActionAccumulator.AddAction(new ClearSelection_Action());
+            startedSelection = true;
+            Helpers.ActionAccumulator.AddAction(new SelectRectangle_Action(pos, size));
         }
 
-        public void MouseUp(object? param)
+        public void EndSelection()
         {
-            if (!mouseIsDown)
+            if (!startedSelection)
                 return;
-            mouseIsDown = false;
-            ProcessToolMouseUp();
+            startedSelection = false;
+            Helpers.ActionAccumulator.AddAction(new EndSelectRectangle_Action());
+            Helpers.ActionAccumulator.AddAction(new MergeLatestChanges_Action(2));
         }
 
-        private void ProcessToolMouseUp()
+        public void ForceRefreshView()
         {
-            if (startedDrawingRect)
-            {
-                startedDrawingRect = false;
-                ActionAccumulator.AddAction(new EndDrawRectangle_Action());
-            }
-            if (startedSelectingRect)
-            {
-                startedSelectingRect = false;
-                ActionAccumulator.AddAction(new EndSelectRectangle_Action());
-                ActionAccumulator.AddAction(new MergeLatestChanges_Action(2));
-            }
+            owner.View?.ForceRefreshFinalImage();
         }
 
-        public void ClearSelection(object? param)
+        private void ClearSelection(object? param)
         {
-            ActionAccumulator.AddAction(new ClearSelection_Action());
+            Helpers.ActionAccumulator.AddAction(new ClearSelection_Action());
         }
 
-        public void DeleteStructureMember(object? param)
+        private void DeleteStructureMember(object? param)
         {
             if (SelectedStructureMember is not null)
-                ActionAccumulator.AddAction(new DeleteStructureMember_Action(SelectedStructureMember.GuidValue));
+                Helpers.ActionAccumulator.AddAction(new DeleteStructureMember_Action(SelectedStructureMember.GuidValue));
         }
 
-        public void Undo(object? param)
+        private void Undo(object? param)
         {
-            ActionAccumulator.AddAction(new Undo_Action());
+            Helpers.ActionAccumulator.AddAction(new Undo_Action());
         }
 
-        public void Redo(object? param)
+        private void Redo(object? param)
         {
-            ActionAccumulator.AddAction(new Redo_Action());
+            Helpers.ActionAccumulator.AddAction(new Redo_Action());
         }
 
-        public void ResizeCanvas(object? param)
+        private void ResizeCanvas(object? param)
         {
-            ActionAccumulator.AddAction(new ResizeCanvas_Action(new(ResizeWidth, ResizeHeight)));
+            Helpers.ActionAccumulator.AddAction(new ResizeCanvas_Action(new(ResizeWidth, ResizeHeight)));
         }
 
         private void ChangeSelectedItem(object? param)
@@ -268,22 +218,30 @@ namespace PixiEditorPrototype.ViewModels
             if (selected.Count < 2)
                 return;
 
-            var (child, parent) = StructureHelper.FindChildAndParentOrThrow(selected[0]);
+            var (child, parent) = Helpers.StructureHelper.FindChildAndParentOrThrow(selected[0]);
             int index = parent.Children.IndexOf(child);
             Guid newGuid = Guid.NewGuid();
 
             //make a new layer, put combined image onto it, delete layers that were merged
-            ActionAccumulator.AddAction(new CreateStructureMember_Action(parent.GuidValue, newGuid, index, StructureMemberType.Layer));
-            ActionAccumulator.AddAction(new SetStructureMemberName_Action(child.Name + "-comb", newGuid));
-            ActionAccumulator.AddAction(new CombineStructureMembersOnto_Action(newGuid, selected.ToHashSet()));
+            Helpers.ActionAccumulator.AddAction(new CreateStructureMember_Action(parent.GuidValue, newGuid, index, StructureMemberType.Layer));
+            Helpers.ActionAccumulator.AddAction(new SetStructureMemberName_Action(child.Name + "-comb", newGuid));
+            Helpers.ActionAccumulator.AddAction(new CombineStructureMembersOnto_Action(newGuid, selected.ToHashSet()));
             foreach (var member in selected)
-                ActionAccumulator.AddAction(new DeleteStructureMember_Action(member));
-            ActionAccumulator.AddAction(new MergeLatestChanges_Action(3 + selected.Count));
+                Helpers.ActionAccumulator.AddAction(new DeleteStructureMember_Action(member));
+            Helpers.ActionAccumulator.AddAction(new MergeLatestChanges_Action(3 + selected.Count));
+        }
+
+        private void MoveViewport(object? param)
+        {
+            if (param is null)
+                throw new ArgumentNullException(nameof(param));
+            var args = (ViewportRoutedEventArgs)param;
+            Helpers.ActionAccumulator.AddAction(new MoveViewport_PassthroughAction(args.Center, args.Size / 2, args.Angle, args.RealSize / 2));
         }
 
         private void ClearHistory(object? param)
         {
-            ActionAccumulator.AddAction(new DeleteRecordedChanges_Action());
+            Helpers.ActionAccumulator.AddAction(new DeleteRecordedChanges_Action());
         }
 
         private void AddSelectedMembers(FolderViewModel folder, List<Guid> collection)
@@ -296,12 +254,5 @@ namespace PixiEditorPrototype.ViewModels
                     AddSelectedMembers(innerFolder, collection);
             }
         }
-
-        private void ChangeActiveTool(object? param)
-        {
-            if (param is null)
-                return;
-            activeTool = (Tool)param;
-        }
     }
 }

+ 3 - 2
src/PixiEditorPrototype/ViewModels/FolderViewModel.cs

@@ -1,4 +1,5 @@
-using ChangeableDocument.Changeables.Interfaces;
+using PixiEditor.ChangeableDocument.Changeables.Interfaces;
+using PixiEditorPrototype.Models;
 using System.Collections.ObjectModel;
 
 namespace PixiEditorPrototype.ViewModels
@@ -6,7 +7,7 @@ namespace PixiEditorPrototype.ViewModels
     internal class FolderViewModel : StructureMemberViewModel
     {
         public ObservableCollection<StructureMemberViewModel> Children { get; } = new();
-        public FolderViewModel(DocumentViewModel doc, IReadOnlyFolder member) : base(doc, member)
+        public FolderViewModel(DocumentViewModel doc, DocumentHelpers helpers, IReadOnlyFolder member) : base(doc, helpers, member)
         {
         }
 

+ 3 - 2
src/PixiEditorPrototype/ViewModels/LayerViewModel.cs

@@ -1,10 +1,11 @@
-using ChangeableDocument.Changeables.Interfaces;
+using PixiEditor.ChangeableDocument.Changeables.Interfaces;
+using PixiEditorPrototype.Models;
 
 namespace PixiEditorPrototype.ViewModels
 {
     internal class LayerViewModel : StructureMemberViewModel
     {
-        public LayerViewModel(DocumentViewModel doc, IReadOnlyLayer layer) : base(doc, layer)
+        public LayerViewModel(DocumentViewModel doc, DocumentHelpers helpers, IReadOnlyLayer layer) : base(doc, helpers, layer)
         {
         }
     }

+ 12 - 9
src/PixiEditorPrototype/ViewModels/StructureMemberViewModel.cs

@@ -1,5 +1,6 @@
-using ChangeableDocument.Actions.Properties;
-using ChangeableDocument.Changeables.Interfaces;
+using PixiEditor.ChangeableDocument.Actions.Properties;
+using PixiEditor.ChangeableDocument.Changeables.Interfaces;
+using PixiEditorPrototype.Models;
 using System;
 using System.ComponentModel;
 
@@ -10,17 +11,18 @@ namespace PixiEditorPrototype.ViewModels
         private IReadOnlyStructureMember member;
         public event PropertyChangedEventHandler? PropertyChanged;
         public DocumentViewModel Document { get; }
+        private DocumentHelpers Helpers { get; }
 
         public string Name
         {
             get => member.Name;
-            set => Document.ActionAccumulator.AddAction(new SetStructureMemberName_Action(value, member.GuidValue));
+            set => Helpers.ActionAccumulator.AddAction(new SetStructureMemberName_Action(value, member.GuidValue));
         }
 
         public bool IsVisible
         {
             get => member.IsVisible;
-            set => Document.ActionAccumulator.AddAction(new SetStructureMemberVisibility_Action(value, member.GuidValue));
+            set => Helpers.ActionAccumulator.AddAction(new SetStructureMemberVisibility_Action(value, member.GuidValue));
         }
 
         public bool IsSelected { get; set; }
@@ -47,26 +49,27 @@ namespace PixiEditorPrototype.ViewModels
             PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
         }
 
-        public StructureMemberViewModel(DocumentViewModel doc, IReadOnlyStructureMember member)
+        public StructureMemberViewModel(DocumentViewModel doc, DocumentHelpers helpers, IReadOnlyStructureMember member)
         {
             this.member = member;
             Document = doc;
-            MoveUpCommand = new(_ => Document.StructureHelper.MoveStructureMember(GuidValue, false));
-            MoveDownCommand = new(_ => Document.StructureHelper.MoveStructureMember(GuidValue, true));
+            Helpers = helpers;
+            MoveUpCommand = new(_ => Helpers.StructureHelper.MoveStructureMember(GuidValue, false));
+            MoveDownCommand = new(_ => Helpers.StructureHelper.MoveStructureMember(GuidValue, true));
             UpdateOpacityCommand = new(UpdateOpacity);
             EndOpacityUpdateCommand = new(EndOpacityUpdate);
         }
 
         private void EndOpacityUpdate(object? opacity)
         {
-            Document.ActionAccumulator.AddAction(new EndOpacityChange_Action());
+            Helpers.ActionAccumulator.AddAction(new EndOpacityChange_Action());
         }
 
         private void UpdateOpacity(object? opacity)
         {
             if (opacity is not double value)
                 throw new ArgumentException("The passed value isn't a double");
-            Document.ActionAccumulator.AddAction(new OpacityChange_Action(GuidValue, (float)value));
+            Helpers.ActionAccumulator.AddAction(new OpacityChange_Action(GuidValue, (float)value));
         }
     }
 }

+ 134 - 0
src/PixiEditorPrototype/ViewModels/ViewModelMain.cs

@@ -0,0 +1,134 @@
+using ChunkyImageLib.DataHolders;
+using PixiEditor.Zoombox;
+using PixiEditorPrototype.Models;
+using PixiEditorPrototype.Views;
+using SkiaSharp;
+using System.ComponentModel;
+using System.Windows.Input;
+using System.Windows.Media;
+
+namespace PixiEditorPrototype.ViewModels
+{
+    internal class ViewModelMain : INotifyPropertyChanged
+    {
+        public IMainView? View { get; set; }
+        public DocumentViewModel? ActiveDocument { get; }
+
+        public RelayCommand? MouseDownCommand { get; }
+        public RelayCommand? MouseMoveCommand { get; }
+        public RelayCommand? MouseUpCommand { get; }
+        public RelayCommand? ChangeActiveToolCommand { get; }
+
+        public Color SelectedColor { get; set; } = Colors.Black;
+
+        private bool mouseIsDown = false;
+        private int mouseDownCanvasX = 0;
+        private int mouseDownCanvasY = 0;
+
+        private bool startedDrawingRect = false;
+        private bool startedSelectingRect = false;
+
+        private Tool activeTool = Tool.Rectangle;
+
+        public event PropertyChangedEventHandler? PropertyChanged;
+
+        private bool enableViewportDragging;
+        public bool EnableViewportDragging
+        {
+            get => enableViewportDragging;
+            set
+            {
+                enableViewportDragging = value;
+                PropertyChanged?.Invoke(this, new(nameof(EnableViewportDragging)));
+                PropertyChanged?.Invoke(this, new(nameof(ZoomboxMode)));
+            }
+        }
+
+        public ZoomboxMode ZoomboxMode => enableViewportDragging ? ZoomboxMode.Move : ZoomboxMode.Normal;
+
+        public ViewModelMain()
+        {
+            MouseDownCommand = new RelayCommand(MouseDown);
+            MouseMoveCommand = new RelayCommand(MouseMove);
+            MouseUpCommand = new RelayCommand(MouseUp);
+            ChangeActiveToolCommand = new RelayCommand(ChangeActiveTool);
+
+            ActiveDocument = new DocumentViewModel(this);
+        }
+
+        private void MouseDown(object? param)
+        {
+            if (ActiveDocument is null || EnableViewportDragging)
+                return;
+            mouseIsDown = true;
+            var args = (MouseButtonEventArgs)(param!);
+            var source = (System.Windows.Controls.Image)args.Source;
+            var pos = args.GetPosition(source);
+            mouseDownCanvasX = (int)(pos.X / source.Width * ActiveDocument.BitmapFull.PixelHeight);
+            mouseDownCanvasY = (int)(pos.Y / source.Height * ActiveDocument.BitmapFull.PixelHeight);
+        }
+
+        private void MouseMove(object? param)
+        {
+            if (ActiveDocument is null || !mouseIsDown || EnableViewportDragging)
+                return;
+            var args = (MouseEventArgs)(param!);
+            var source = (System.Windows.Controls.Image)args.Source;
+            var pos = args.GetPosition(source);
+            int curX = (int)(pos.X / source.Width * ActiveDocument.BitmapFull.PixelHeight);
+            int curY = (int)(pos.Y / source.Height * ActiveDocument.BitmapFull.PixelHeight);
+
+            ProcessToolMouseMove(curX, curY);
+        }
+
+        private void ProcessToolMouseMove(int canvasX, int canvasY)
+        {
+            if (activeTool == Tool.Rectangle)
+            {
+                startedDrawingRect = true;
+                ActiveDocument!.StartUpdateRectangle(new ShapeData(
+                            new(mouseDownCanvasX, mouseDownCanvasY),
+                            new(canvasX - mouseDownCanvasX, canvasY - mouseDownCanvasY),
+                            90,
+                            new SKColor(SelectedColor.R, SelectedColor.G, SelectedColor.B, SelectedColor.A),
+                            SKColors.Transparent));
+            }
+            else if (activeTool == Tool.Select)
+            {
+                startedSelectingRect = true;
+                ActiveDocument!.StartUpdateSelection(
+                    new(mouseDownCanvasX, mouseDownCanvasY),
+                    new(canvasX - mouseDownCanvasX, canvasY - mouseDownCanvasY));
+            }
+        }
+
+        private void MouseUp(object? param)
+        {
+            if (ActiveDocument is null || !mouseIsDown || EnableViewportDragging)
+                return;
+            mouseIsDown = false;
+            ProcessToolMouseUp();
+        }
+
+        private void ProcessToolMouseUp()
+        {
+            if (startedDrawingRect)
+            {
+                startedDrawingRect = false;
+                ActiveDocument!.EndRectangle();
+            }
+            if (startedSelectingRect)
+            {
+                startedSelectingRect = false;
+                ActiveDocument!.EndSelection();
+            }
+        }
+
+        private void ChangeActiveTool(object? param)
+        {
+            if (param is null)
+                return;
+            activeTool = (Tool)param;
+        }
+    }
+}

+ 0 - 26
src/PixiEditorPrototype/Views/DocumentView.xaml.cs

@@ -1,26 +0,0 @@
-using PixiEditorPrototype.ViewModels;
-using System.Windows.Controls;
-
-namespace PixiEditorPrototype.Views
-{
-    /// <summary>
-    /// Interaction logic for DocumentView.xaml
-    /// </summary>
-    internal partial class DocumentView : UserControl, IDocumentView
-    {
-        public DocumentView()
-        {
-            InitializeComponent();
-            DataContextChanged += (_, e) =>
-            {
-                if (e.NewValue is not null)
-                    ((DocumentViewModel)e.NewValue).View = this;
-            };
-        }
-
-        public void ForceRefreshFinalImage()
-        {
-            mainImage.InvalidateVisual();
-        }
-    }
-}

+ 1 - 1
src/PixiEditorPrototype/Views/IDocumentView.cs → src/PixiEditorPrototype/Views/IMainView.cs

@@ -1,6 +1,6 @@
 namespace PixiEditorPrototype.Views
 {
-    internal interface IDocumentView
+    internal interface IMainView
     {
         void ForceRefreshFinalImage();
     }

+ 79 - 57
src/PixiEditorPrototype/Views/DocumentView.xaml → src/PixiEditorPrototype/Views/MainWindow.xaml

@@ -1,46 +1,48 @@
-<UserControl x:Class="PixiEditorPrototype.Views.DocumentView" x:ClassModifier="internal"
-             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
-             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
-             xmlns:local="clr-namespace:PixiEditorPrototype.Views"
-             xmlns:pe="clr-namespace:PixiEditorPrototype"
-             xmlns:models="clr-namespace:PixiEditorPrototype.Models"
-             xmlns:colorpicker="clr-namespace:ColorPicker;assembly=ColorPicker"
-             xmlns:behaviors="clr-namespace:PixiEditorPrototype.Behaviors"
-             xmlns:converters="clr-namespace:PixiEditorPrototype.Converters"
-             xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
-             xmlns:cmd="mvvm"
-             xmlns:vm="clr-namespace:PixiEditorPrototype.ViewModels"
-             mc:Ignorable="d" 
-             d:DataContext="{d:DesignInstance Type=vm:DocumentViewModel, IsDesignTimeCreatable=True}"
-             d:DesignHeight="576" d:DesignWidth="1024">
-    <UserControl.Resources>
-        <converters:IndexToChunkResolutionConverter x:Key="IndexToChunkResolutionConverter"/>
-    </UserControl.Resources>
+<Window x:Class="PixiEditorPrototype.Views.MainWindow" x:ClassModifier="internal"
+        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        xmlns:vm="clr-namespace:PixiEditorPrototype.ViewModels"
+        xmlns:conv="clr-namespace:PixiEditorPrototype.Converters" 
+        xmlns:beh="clr-namespace:PixiEditorPrototype.Behaviors"
+        xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
+        xmlns:pe="clr-namespace:PixiEditorPrototype"
+        xmlns:zoombox="clr-namespace:PixiEditor.Zoombox;assembly=PixiEditor.Zoombox"
+        xmlns:models="clr-namespace:PixiEditorPrototype.Models"
+        xmlns:colorpicker="clr-namespace:ColorPicker;assembly=ColorPicker"
+        mc:Ignorable="d"
+        Title="MainWindow" Height="576" Width="1024">
+    <Window.DataContext>
+        <vm:ViewModelMain/>
+    </Window.DataContext>
+    <Window.Resources>
+        <conv:IndexToChunkResolutionConverter x:Key="IndexToChunkResolutionConverter"/>
+    </Window.Resources>
     <DockPanel Background="Gray">
         <Border BorderThickness="1" Background="White" BorderBrush="Black" Width="280" DockPanel.Dock="Right" Margin="5">
             <DockPanel>
                 <StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
-                    <Button Margin="5" Command="{Binding CreateNewLayerCommand}" Width="80">New Layer</Button>
-                    <Button Margin="5" Command="{Binding CreateNewFolderCommand}" Width="80">New Folder</Button>
-                    <Button Margin="5" Command="{Binding DeleteStructureMemberCommand}" Width="80">Delete</Button>
+                    <Button Margin="5" Command="{Binding ActiveDocument.CreateNewLayerCommand}" Width="80">New Layer</Button>
+                    <Button Margin="5" Command="{Binding ActiveDocument.CreateNewFolderCommand}" Width="80">New Folder</Button>
+                    <Button Margin="5" Command="{Binding ActiveDocument.DeleteStructureMemberCommand}" Width="80">Delete</Button>
                 </StackPanel>
                 <DockPanel DockPanel.Dock="Top" HorizontalAlignment="Stretch" Margin="0,0,0,5">
-                    <Button Width="80" Margin="5,0" Command="{Binding CombineCommand}">Merge</Button>
-                    <TextBlock Text="{Binding SelectedStructureMember.Opacity, StringFormat=N2}" Margin="5,0" DockPanel.Dock="Right" VerticalAlignment="Center" TextAlignment="Center" d:Text="1.00" Width="30"></TextBlock>
+                    <Button Width="80" Margin="5,0" Command="{Binding ActiveDocument.CombineCommand}">Merge</Button>
+                    <TextBlock Text="{Binding ActiveDocument.SelectedStructureMember.Opacity, StringFormat=N2}" 
+                           Margin="5,0" DockPanel.Dock="Right" VerticalAlignment="Center" TextAlignment="Center" d:Text="1.00" Width="30"/>
                     <Slider Minimum="0" Maximum="1" SmallChange="0.01" LargeChange="0.1" IsSnapToTickEnabled="True" TickFrequency="0.01" x:Name="opacitySlider"
                             VerticalAlignment="Center" HorizontalAlignment="Stretch"
-                            Value="{Binding SelectedStructureMember.Opacity, Mode=OneWay}">
+                            Value="{Binding ActiveDocument.SelectedStructureMember.Opacity, Mode=OneWay}">
                         <i:Interaction.Behaviors>
-                            <behaviors:SliderUpdateBehavior
-                                DragValueChanged="{Binding SelectedStructureMember.UpdateOpacityCommand}"
-                                DragEnded="{Binding SelectedStructureMember.EndOpacityUpdateCommand}"
+                            <beh:SliderUpdateBehavior
+                                DragValueChanged="{Binding ActiveDocument.SelectedStructureMember.UpdateOpacityCommand}"
+                                DragEnded="{Binding ActiveDocument.SelectedStructureMember.EndOpacityUpdateCommand}"
                                 ValueFromSlider="{Binding ElementName=opacitySlider, Path=Value}"/>
                         </i:Interaction.Behaviors>
                     </Slider>
                 </DockPanel>
-                <TreeView ItemsSource="{Binding StructureRoot.Children}">
+                <TreeView ItemsSource="{Binding ActiveDocument.StructureRoot.Children}">
                     <TreeView.ItemsPanel>
                         <ItemsPanelTemplate>
                             <pe:ReversedOrderStackPanel/>
@@ -59,7 +61,7 @@
                     </TreeView.ItemContainerStyle>
                     <i:Interaction.Triggers>
                         <i:EventTrigger EventName="SelectedItemChanged">
-                            <i:InvokeCommandAction Command="{Binding ChangeSelectedItemCommand}" PassEventArgsToCommand="True"/>
+                            <i:InvokeCommandAction Command="{Binding ActiveDocument.ChangeSelectedItemCommand}" PassEventArgsToCommand="True"/>
                         </i:EventTrigger>
                     </i:Interaction.Triggers>
                     <TreeView.Resources>
@@ -96,21 +98,15 @@
         <Border BorderThickness="1" Background="White" BorderBrush="Black" DockPanel.Dock="Top" Margin="5">
             <DockPanel>
                 <StackPanel Orientation="Horizontal" Background="White">
-                    <Button Width="50" Margin="5" Command="{Binding UndoCommand}">Undo</Button>
-                    <Button Width="50" Margin="5" Command="{Binding RedoCommand}">Redo</Button>
-                    <Button Width="100" Margin="5" Command="{Binding ClearSelectionCommand}">Clear selection</Button>
-                    <Button Width="120" Margin="5" Command="{Binding ClearHistoryCommand}">Clear undo history</Button>
-                    <ComboBox SelectedIndex="{Binding ResolutionFromView, Converter={StaticResource IndexToChunkResolutionConverter}, Mode=OneWayToSource}" Width="70" Margin="5">
-                        <ComboBoxItem>Full</ComboBoxItem>
-                        <ComboBoxItem>Half</ComboBoxItem>
-                        <ComboBoxItem>Quarter</ComboBoxItem>
-                        <ComboBoxItem>Eighth</ComboBoxItem>
-                    </ComboBox>
+                    <Button Width="50" Margin="5" Command="{Binding ActiveDocument.UndoCommand}">Undo</Button>
+                    <Button Width="50" Margin="5" Command="{Binding ActiveDocument.RedoCommand}">Redo</Button>
+                    <Button Width="100" Margin="5" Command="{Binding ActiveDocument.ClearSelectionCommand}">Clear selection</Button>
+                    <Button Width="120" Margin="5" Command="{Binding ActiveDocument.ClearHistoryCommand}">Clear undo history</Button>
                 </StackPanel>
                 <StackPanel DockPanel.Dock="Right" Orientation="Horizontal" HorizontalAlignment="Right">
-                    <TextBox Width="30" Margin="5" Text="{Binding ResizeWidth}"/>
-                    <TextBox Width="30" Margin="5" Text="{Binding ResizeHeight}"/>
-                    <Button Width="50" Margin="5" Command="{Binding ResizeCanvasCommand}">Resize</Button>
+                    <TextBox Width="30" Margin="5" Text="{Binding ActiveDocument.ResizeWidth}"/>
+                    <TextBox Width="30" Margin="5" Text="{Binding ActiveDocument.ResizeHeight}"/>
+                    <Button Width="50" Margin="5" Command="{Binding ActiveDocument.ResizeCanvasCommand}">Resize</Button>
                 </StackPanel>
             </DockPanel>
         </Border>
@@ -119,22 +115,48 @@
                 <Button Width="50" Margin="5" Command="{Binding ChangeActiveToolCommand}" CommandParameter="{x:Static models:Tool.Rectangle}">Rect</Button>
                 <Button Width="50" Margin="5" Command="{Binding ChangeActiveToolCommand}" CommandParameter="{x:Static models:Tool.Select}">Select</Button>
                 <colorpicker:PortableColorPicker Margin="5" SelectedColor="{Binding SelectedColor, Mode=TwoWay}" Width="30" Height="30"/>
+                <CheckBox Margin="5" x:Name="viewportMoveCheckbox" IsChecked="{Binding EnableViewportDragging}">Move</CheckBox>
             </StackPanel>
         </Border>
-        <Border BorderThickness="1" Background="Transparent" BorderBrush="Black" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5">
-            <Image Margin="5" Source="{Binding RenderBitmap}" Width="400" Height="400" RenderOptions.BitmapScalingMode="NearestNeighbor" x:Name="mainImage">
+
+        <Grid>
+            <zoombox:Zoombox x:Name="zoombox" ZoomMode="{Binding ZoomboxMode}">
                 <i:Interaction.Triggers>
-                    <i:EventTrigger EventName="MouseDown">
-                        <i:InvokeCommandAction Command="{Binding MouseDownCommand}" PassEventArgsToCommand="True"/>
-                    </i:EventTrigger>
-                    <i:EventTrigger EventName="MouseMove">
-                        <i:InvokeCommandAction Command="{Binding MouseMoveCommand}" PassEventArgsToCommand="True"/>
-                    </i:EventTrigger>
-                    <i:EventTrigger EventName="MouseUp">
-                        <i:InvokeCommandAction Command="{Binding MouseUpCommand}" PassEventArgsToCommand="True"/>
+                    <i:EventTrigger EventName="ViewportMoved">
+                        <i:InvokeCommandAction Command="{Binding ActiveDocument.MoveViewportCommand}" PassEventArgsToCommand="True"/>
                     </i:EventTrigger>
                 </i:Interaction.Triggers>
-            </Image>
-        </Border>
+                <Border BorderThickness="1" Background="White" BorderBrush="Black" HorizontalAlignment="Center" VerticalAlignment="Center">
+                    <Image Source="{Binding ActiveDocument.RenderBitmap}" Focusable="True"
+                       Width="{Binding ActiveDocument.BitmapFull.PixelWidth}" Height="{Binding ActiveDocument.BitmapFull.PixelHeight}"
+                       RenderOptions.BitmapScalingMode="NearestNeighbor">
+                        <i:Interaction.Triggers>
+                            <i:EventTrigger EventName="MouseDown">
+                                <i:InvokeCommandAction Command="{Binding MouseDownCommand}" PassEventArgsToCommand="True"/>
+                            </i:EventTrigger>
+                            <i:EventTrigger EventName="MouseMove">
+                                <i:InvokeCommandAction Command="{Binding MouseMoveCommand}" PassEventArgsToCommand="True"/>
+                            </i:EventTrigger>
+                            <i:EventTrigger EventName="MouseUp">
+                                <i:InvokeCommandAction Command="{Binding MouseUpCommand}" PassEventArgsToCommand="True"/>
+                            </i:EventTrigger>
+                        </i:Interaction.Triggers>
+                    </Image>
+                </Border>
+            </zoombox:Zoombox>
+            <Grid Focusable="False">
+                <Grid.ColumnDefinitions>
+                    <ColumnDefinition Width="1*"/>
+                    <ColumnDefinition Width="2*"/>
+                    <ColumnDefinition Width="1*"/>
+                </Grid.ColumnDefinitions>
+                <Grid.RowDefinitions>
+                    <RowDefinition Height="1*"/>
+                    <RowDefinition Height="2*"/>
+                    <RowDefinition Height="1*"/>
+                </Grid.RowDefinitions>
+                <Border BorderBrush="Red" Grid.Row="1" Grid.Column="1" BorderThickness="1"/>
+            </Grid>
+        </Grid>
     </DockPanel>
-</UserControl>
+</Window>

+ 19 - 0
src/PixiEditorPrototype/Views/MainWindow.xaml.cs

@@ -0,0 +1,19 @@
+using PixiEditorPrototype.ViewModels;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace PixiEditorPrototype.Views
+{
+    internal partial class MainWindow : Window, IMainView
+    {
+        public MainWindow()
+        {
+            InitializeComponent();
+            ((ViewModelMain)DataContext).View = this;
+        }
+        public void ForceRefreshFinalImage()
+        {
+            ((Image?)((Border?)zoombox.AdditionalContent)?.Child)?.InvalidateVisual();
+        }
+    }
+}