Przeglądaj źródła

Rewrote zoombox and reworked scene to be a zoombox

Krzysztof Krysiński 1 rok temu
rodzic
commit
92e2c63ce0

+ 15 - 13
src/PixiEditor.AvaloniaUI/Views/Main/ViewportControls/Viewport.axaml

@@ -121,21 +121,23 @@
         <visuals:Scene
             Focusable="False" Name="scene"
             ZIndex="1"
-            Width="{Binding RealDimensions.X, ElementName=vpUc}"
-            Height="{Binding RealDimensions.Y, ElementName=vpUc}"
             Surface="{Binding TargetBitmap, ElementName=vpUc}"
-            Scale="{Binding Scale, ElementName=zoombox, Mode=OneWay}"
             Document="{Binding Document, ElementName=vpUc, Mode=OneWay}"
-            ContentPosition="{Binding CanvasPos, ElementName=zoombox, Mode=OneWay}"
-            Angle="{Binding RotateTransformAngle, ElementName=zoombox, Mode=OneWay}"
-            FlipX="{Binding FlipX, ElementName=zoombox, Mode=OneWay}"
-            FlipY="{Binding FlipY, ElementName=zoombox, Mode=OneWay}"
+            UseTouchGestures="{Binding UseTouchGestures, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWay}"
+            Scale="{Binding ZoomboxScale, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWayToSource}"
+            Center="{Binding Center, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWayToSource}"
+            Angle="{Binding Angle, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWayToSource}"
+            RealDimensions="{Binding RealDimensions, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWayToSource}"
+            Dimensions="{Binding Dimensions, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWayToSource}"
+            ZoomMode="{Binding ZoomMode, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}"
+            ZoomOutOnClick="{Binding ZoomOutOnClick, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}"
+            FlipX="{Binding FlipX, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}"
+            FlipY="{Binding FlipY, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}"
             ActiveOverlays="{Binding ElementName=vpUc, Path=ActiveOverlays}"
             FadeOut="{Binding Source={viewModels:ToolVM ColorPickerToolViewModel}, Path=PickOnlyFromReferenceLayer, Mode=OneWay}"
             CheckerImagePath="/Images/CheckerTile.png"
-            ui1:RenderOptionsBindable.BitmapInterpolationMode="{Binding Scale, Converter={converters:ScaleToBitmapScalingModeConverter}, ElementName=zoombox}">
-        </visuals:Scene>
-        <zoombox:Zoombox
+            ui1:RenderOptionsBindable.BitmapInterpolationMode="{Binding Scale, Converter={converters:ScaleToBitmapScalingModeConverter}, RelativeSource={RelativeSource Self}}"/>
+        <!--<zoombox:Zoombox
             Tag="{Binding ElementName=vpUc}"
             x:Name="zoombox"
             UseTouchGestures="{Binding UseTouchGestures, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWay}"
@@ -149,7 +151,7 @@
             FlipX="{Binding FlipX, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}"
             FlipY="{Binding FlipY, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}">
             <zoombox:Zoombox.AdditionalContent>
-                <!--<Border
+                <Border
                     d:Width="64"
                     d:Height="64"
                     HorizontalAlignment="Center"
@@ -182,9 +184,9 @@
                             </Rectangle.Margin>
                         </Rectangle>
                     </Grid>
-                </Border>-->
+                </Border>
             </zoombox:Zoombox.AdditionalContent>
-        </zoombox:Zoombox>
+        </zoombox:Zoombox>-->
         <overlays:ReferenceLayerOverlay SizeChanged="OnReferenceImageSizeChanged"
                                         ReferenceLayer="{Binding Document.ReferenceLayerViewModel}"
                                         ReferenceLayerScale="{Binding ReferenceLayerScale}"

+ 21 - 40
src/PixiEditor.AvaloniaUI/Views/Main/ViewportControls/Viewport.axaml.cs

@@ -1,32 +1,21 @@
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
+using System.Collections.ObjectModel;
 using System.ComponentModel;
 using System.Windows.Input;
 using Avalonia;
 using Avalonia.Controls;
-using Avalonia.Data;
-using Avalonia.Data.Core;
 using Avalonia.Input;
 using Avalonia.Interactivity;
-using Avalonia.Media.Imaging;
 using ChunkyImageLib;
 using ChunkyImageLib.DataHolders;
-using PixiEditor.AvaloniaUI.Helpers.Converters;
+using PixiEditor.AvaloniaUI.Helpers;
 using PixiEditor.AvaloniaUI.Helpers.UI;
-using PixiEditor.AvaloniaUI.Models.Commands.XAML;
 using PixiEditor.AvaloniaUI.Models.Controllers.InputDevice;
 using PixiEditor.AvaloniaUI.Models.DocumentModels;
 using PixiEditor.AvaloniaUI.Models.Position;
-using PixiEditor.AvaloniaUI.ViewModels;
 using PixiEditor.AvaloniaUI.ViewModels.Document;
-using PixiEditor.AvaloniaUI.ViewModels.Tools.ToolSettings.Toolbars;
 using PixiEditor.AvaloniaUI.Views.Overlays;
-using PixiEditor.AvaloniaUI.Views.Overlays.BrushShapeOverlay;
-using PixiEditor.AvaloniaUI.Views.Overlays.SelectionOverlay;
-using PixiEditor.AvaloniaUI.Views.Overlays.SymmetryOverlay;
 using PixiEditor.AvaloniaUI.Views.Visuals;
 using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.DrawingApi.Core.Surface;
 using PixiEditor.Zoombox;
 using Point = Avalonia.Point;
 
@@ -191,7 +180,7 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
 
     public double ZoomboxScale
     {
-        get => zoombox?.Scale ?? 1;
+        get => scene?.Scale ?? 1;
         // ReSharper disable once ValueParameterNotUsed
         set
         {
@@ -288,8 +277,6 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
             ? (Document.ReferenceLayerViewModel.ReferenceShapeBindable.RectSize.X / (double)Document.ReferenceLayerViewModel.ReferenceBitmap.Size.X)
             : 1);
 
-    public PixiEditor.Zoombox.Zoombox Zoombox => zoombox;
-
     public ObservableCollection<Overlay> ActiveOverlays { get; } = new();
 
     public Guid GuidValue { get; } = Guid.NewGuid();
@@ -396,22 +383,20 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
 
     private void Image_MouseDown(object? sender, PointerPressedEventArgs e)
     {
+        if(Document is null)
+            return;
+
         bool isMiddle = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed;
         HandleMiddleMouse(isMiddle);
 
         if (MouseDownCommand is null)
             return;
 
-        MouseButton mouseButton = e.GetCurrentPoint(this).Properties.PointerUpdateKind switch
-        {
-            PointerUpdateKind.LeftButtonPressed => MouseButton.Left,
-            PointerUpdateKind.RightButtonPressed => MouseButton.Right,
-            _ => MouseButton.Middle
-        };
+        MouseButton mouseButton = e.GetMouseButton(this);
 
-        Point pos = e.GetPosition(Scene);
-        VecD conv = new VecD(pos.X, pos.Y);
-        MouseOnCanvasEventArgs? parameter = new MouseOnCanvasEventArgs(mouseButton, conv);
+        var pos = e.GetPosition(Scene);
+        VecD scenePos = Scene.ToZoomboxSpace(new VecD(pos.X, pos.Y));
+        MouseOnCanvasEventArgs? parameter = new MouseOnCanvasEventArgs(mouseButton, scenePos);
 
         if (MouseDownCommand.CanExecute(parameter))
             MouseDownCommand.Execute(parameter);
@@ -422,14 +407,9 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
         if (MouseMoveCommand is null)
             return;
         Point pos = e.GetPosition(Scene);
-        VecD conv = new VecD(pos.X, pos.Y);
+        VecD conv = Scene.ToZoomboxSpace(new VecD(pos.X, pos.Y));
 
-        MouseButton mouseButton = e.GetCurrentPoint(this).Properties.PointerUpdateKind switch
-        {
-            PointerUpdateKind.LeftButtonPressed => MouseButton.Left,
-            PointerUpdateKind.RightButtonPressed => MouseButton.Right,
-            _ => MouseButton.Middle
-        };
+        MouseButton mouseButton = e.GetMouseButton(this);
 
         MouseOnCanvasEventArgs parameter = new(mouseButton, conv);
 
@@ -443,36 +423,37 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
             return;
 
         Point pos = e.GetPosition(Scene);
-        MouseOnCanvasEventArgs parameter = new(e.InitialPressMouseButton, new VecD(pos.X, pos.Y));
+        VecD conv = Scene.ToZoomboxSpace(new VecD(pos.X, pos.Y));
+        MouseOnCanvasEventArgs parameter = new(e.InitialPressMouseButton, conv);
         if (MouseUpCommand.CanExecute(parameter))
             MouseUpCommand.Execute(parameter);
     }
 
     private void CenterZoomboxContent(object? sender, VecI args)
     {
-        zoombox.CenterContent(args);
+        scene.CenterContent(args);
     }
 
     private void ZoomZoomboxContent(object? sender, double delta)
     {
-        zoombox.ZoomIntoCenter(delta);
+        scene.ZoomIntoCenter(delta);
     }
 
     private void OnImageLoaded(object sender, EventArgs e)
     {
-        zoombox.CenterContent();
+        scene.CenterContent();
     }
 
     private void OnMainImageSizeChanged(object? sender, SizeChangedEventArgs e)
     {
-        if (zoombox.Dimensions is { X: 0, Y: 0 }) return;
-        zoombox.CenterContent(new VecD(e.NewSize.Width, e.NewSize.Height));
+        if (scene.Dimensions is { X: 0, Y: 0 }) return;
+        scene.CenterContent(new VecD(e.NewSize.Width, e.NewSize.Height));
     }
     
     private void ResetViewportClicked(object sender, RoutedEventArgs e)
     {
-        zoombox.Angle = 0;
-        zoombox.CenterContent();
+        scene.Angle = 0;
+        scene.CenterContent();
     }
 
     private static void CenterViewportTriggerChanged(AvaloniaPropertyChangedEventArgs<ExecutionTrigger<VecI>> e)

+ 57 - 104
src/PixiEditor.AvaloniaUI/Views/Visuals/Scene.cs

@@ -27,30 +27,15 @@ using Point = Avalonia.Point;
 
 namespace PixiEditor.AvaloniaUI.Views.Visuals;
 
-internal class Scene : Control, ICustomHitTest
+internal class Scene : Zoombox.Zoombox, ICustomHitTest
 {
     public static readonly StyledProperty<Surface> SurfaceProperty = AvaloniaProperty.Register<SurfaceControl, Surface>(
         nameof(Surface));
 
-    public static readonly StyledProperty<double> ScaleProperty = AvaloniaProperty.Register<Scene, double>(
-        nameof(Scale), 1);
-
-    public static readonly StyledProperty<VecI> ContentPositionProperty = AvaloniaProperty.Register<Scene, VecI>(
-        nameof(ContentPosition));
-
     public static readonly StyledProperty<DocumentViewModel> DocumentProperty =
         AvaloniaProperty.Register<Scene, DocumentViewModel>(
             nameof(Document));
 
-    public static readonly StyledProperty<double> AngleProperty = AvaloniaProperty.Register<Scene, double>(
-        nameof(Angle), 0);
-
-    public static readonly StyledProperty<bool> FlipXProperty = AvaloniaProperty.Register<Scene, bool>(
-        nameof(FlipX), false);
-
-    public static readonly StyledProperty<bool> FlipYProperty = AvaloniaProperty.Register<Scene, bool>(
-        nameof(FlipY), false);
-
     public static readonly StyledProperty<bool> FadeOutProperty = AvaloniaProperty.Register<Scene, bool>(
         nameof(FadeOut), false);
 
@@ -79,48 +64,18 @@ internal class Scene : Control, ICustomHitTest
         set => SetValue(FadeOutProperty, value);
     }
 
-    public double Angle
-    {
-        get => GetValue(AngleProperty);
-        set => SetValue(AngleProperty, value);
-    }
-
     public DocumentViewModel Document
     {
         get => GetValue(DocumentProperty);
         set => SetValue(DocumentProperty, value);
     }
 
-    public VecI ContentPosition
-    {
-        get => GetValue(ContentPositionProperty);
-        set => SetValue(ContentPositionProperty, value);
-    }
-
-    public double Scale
-    {
-        get => GetValue(ScaleProperty);
-        set => SetValue(ScaleProperty, value);
-    }
-
     public Surface Surface
     {
         get => GetValue(SurfaceProperty);
         set => SetValue(SurfaceProperty, value);
     }
 
-    public bool FlipX
-    {
-        get { return (bool)GetValue(FlipXProperty); }
-        set { SetValue(FlipXProperty, value); }
-    }
-
-    public bool FlipY
-    {
-        get { return (bool)GetValue(FlipYProperty); }
-        set { SetValue(FlipYProperty, value); }
-    }
-
     private Bitmap? checkerBitmap;
     private Overlay? capturedOverlay;
 
@@ -129,14 +84,10 @@ internal class Scene : Control, ICustomHitTest
     static Scene()
     {
         AffectsRender<Scene>(BoundsProperty, WidthProperty, HeightProperty, ScaleProperty, AngleProperty, FlipXProperty,
-            FlipYProperty, ContentPositionProperty, DocumentProperty, SurfaceProperty);
-        BoundsProperty.Changed.AddClassHandler<Scene>(BoundsChanged);
-        ContentPositionProperty.Changed.AddClassHandler<Scene>(Rerender);
-        ScaleProperty.Changed.AddClassHandler<Scene>(Rerender);
-        FlipXProperty.Changed.AddClassHandler<Scene>(Rerender);
-        FlipYProperty.Changed.AddClassHandler<Scene>(Rerender);
-        AngleProperty.Changed.AddClassHandler<Scene>(Rerender);
+            FlipYProperty, DocumentProperty, SurfaceProperty, ActiveOverlaysProperty);
+
         FadeOutProperty.Changed.AddClassHandler<Scene>(FadeOutChanged);
+        SurfaceProperty.Changed.AddClassHandler<Scene>(SurfaceChanged);
         CheckerImagePathProperty.Changed.AddClassHandler<Scene>(CheckerImagePathChanged);
         ActiveOverlaysProperty.Changed.AddClassHandler<Scene>(ActiveOverlaysChanged);
     }
@@ -167,16 +118,16 @@ internal class Scene : Control, ICustomHitTest
             angle = 360 - angle;
         }
 
-        context.PushTransform(Matrix.CreateTranslation(ContentPosition.X, ContentPosition.Y));
-        context.PushTransform(Matrix.CreateScale(finalScale, finalScale));
-        context.PushTransform(Matrix.CreateRotation(MathUtil.AngleToRadians(angle)));
-        context.PushTransform(Matrix.CreateScale(FlipX ? -1 : 1, FlipY ? -1 : 1));
-
-        var operation = new DrawSceneOperation(Surface, Document, ContentPosition, finalScale, Angle, FlipX, FlipY,
+        var operation = new DrawSceneOperation(Surface, Document, CanvasPos, finalScale, Angle, FlipX, FlipY,
+            new Rect(CanvasPos.X, CanvasPos.Y, ContentDimensions.X * finalScale, ContentDimensions.Y * finalScale),
             Bounds,
             Opacity, (SKBitmap)checkerBitmap.Native);
         context.Custom(operation);
 
+        context.PushTransform(Matrix.CreateTranslation(CanvasPos.X, CanvasPos.Y));
+        context.PushTransform(Matrix.CreateScale(finalScale, finalScale));
+        context.PushTransform(Matrix.CreateRotation(MathUtil.AngleToRadians(angle)));
+        context.PushTransform(Matrix.CreateScale(FlipX ? -1 : 1, FlipY ? -1 : 1));
 
         if (ActiveOverlays != null)
         {
@@ -347,7 +298,7 @@ internal class Scene : Control, ICustomHitTest
         transform = transform.Append(Matrix.CreateRotation(MathUtil.AngleToRadians((float)Angle)));
         transform = transform.Append(Matrix.CreateScale(FlipX ? -1 : 1, FlipY ? -1 : 1));
         transform = transform.Append(Matrix.CreateScale(finalScale, finalScale));
-        transform = transform.Append(Matrix.CreateTranslation(ContentPosition.X, ContentPosition.Y));
+        transform = transform.Append(Matrix.CreateTranslation(CanvasPos.X, CanvasPos.Y));
         return transform;
     }
 
@@ -385,35 +336,6 @@ internal class Scene : Control, ICustomHitTest
         mouseOverOverlays.Add(overlay);
     }
 
-    private static void BoundsChanged(Scene sender, AvaloniaPropertyChangedEventArgs e)
-    {
-        sender.InvalidateVisual();
-    }
-
-    private static void Rerender(Scene scene, AvaloniaPropertyChangedEventArgs e)
-    {
-        scene.InvalidateVisual();
-    }
-
-    private static void FadeOutChanged(Scene scene, AvaloniaPropertyChangedEventArgs e)
-    {
-        scene.Opacity = e.NewValue is true ? 0 : 1;
-    }
-
-    private static void ActiveOverlaysChanged(Scene scene, AvaloniaPropertyChangedEventArgs e)
-    {
-        scene.InvalidateVisual();
-        if (e.OldValue is ObservableCollection<Overlay> oldOverlays)
-        {
-            oldOverlays.CollectionChanged -= scene.OverlayCollectionChanged;
-        }
-
-        if (e.NewValue is ObservableCollection<Overlay> newOverlays)
-        {
-            newOverlays.CollectionChanged += scene.OverlayCollectionChanged;
-        }
-    }
-
     private void OverlayCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
     {
         InvalidateVisual();
@@ -438,6 +360,23 @@ internal class Scene : Control, ICustomHitTest
     {
         Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Render);
     }
+    private static void FadeOutChanged(Scene scene, AvaloniaPropertyChangedEventArgs e)
+    {
+        scene.Opacity = e.NewValue is true ? 0 : 1;
+    }
+
+    private static void ActiveOverlaysChanged(Scene scene, AvaloniaPropertyChangedEventArgs e)
+    {
+        if (e.OldValue is ObservableCollection<Overlay> oldOverlays)
+        {
+            oldOverlays.CollectionChanged -= scene.OverlayCollectionChanged;
+        }
+
+        if (e.NewValue is ObservableCollection<Overlay> newOverlays)
+        {
+            newOverlays.CollectionChanged += scene.OverlayCollectionChanged;
+        }
+    }
 
     private static void CheckerImagePathChanged(Scene scene, AvaloniaPropertyChangedEventArgs e)
     {
@@ -451,21 +390,17 @@ internal class Scene : Control, ICustomHitTest
         }
     }
 
-    bool ICustomHitTest.HitTest(Point point)
+    private static void SurfaceChanged(Scene scene, AvaloniaPropertyChangedEventArgs e)
     {
-        if (ActiveOverlays == null) return false;
-
-        foreach (Overlay overlay in ActiveOverlays)
+        if(e.NewValue is Surface surface)
         {
-            if (!overlay.IsVisible) continue;
-            VecD pointInOverlay = ToCanvasSpace(point);
-            if (overlay.TestHit(pointInOverlay))
-            {
-                return true;
-            }
+            scene.ContentDimensions = surface.Size;
         }
+    }
 
-        return false;
+    bool ICustomHitTest.HitTest(Point point)
+    {
+        return Bounds.Contains(point);
     }
 }
 
@@ -473,19 +408,20 @@ internal class DrawSceneOperation : SkiaDrawOperation
 {
     public Surface Surface { get; set; }
     public DocumentViewModel Document { get; set; }
-    public VecI ContentPosition { get; set; }
+    public VecD ContentPosition { get; set; }
     public double Scale { get; set; }
     public double Angle { get; set; }
     public bool FlipX { get; set; }
     public bool FlipY { get; set; }
 
+    public Rect ViewportBounds { get; set; }
     public SKBitmap? CheckerBitmap { get; set; }
 
     private SKPaint _paint = new SKPaint();
     private SKPaint _checkerPaint;
 
-    public DrawSceneOperation(Surface surface, DocumentViewModel document, VecI contentPosition, double scale,
-        double angle, bool flipX, bool flipY, Rect bounds, double opacity, SKBitmap checkerBitmap) : base(bounds)
+    public DrawSceneOperation(Surface surface, DocumentViewModel document, VecD contentPosition, double scale,
+        double angle, bool flipX, bool flipY, Rect dirtyBounds, Rect viewportBounds, double opacity, SKBitmap checkerBitmap) : base(dirtyBounds)
     {
         Surface = surface;
         Document = document;
@@ -494,6 +430,7 @@ internal class DrawSceneOperation : SkiaDrawOperation
         Angle = angle;
         FlipX = flipX;
         FlipY = flipY;
+        ViewportBounds = viewportBounds;
         CheckerBitmap = checkerBitmap;
         _paint.Color = _paint.Color.WithAlpha((byte)(opacity * 255));
 
@@ -525,6 +462,22 @@ internal class DrawSceneOperation : SkiaDrawOperation
             return;
         }
 
+        canvas.Scale((float)Scale, (float)Scale, (float)ContentPosition.X, (float)ContentPosition.Y);
+        float angle = (float)Angle;
+        if (FlipX)
+        {
+            angle = 360 - angle;
+        }
+
+        if (FlipY)
+        {
+            angle = 360 - angle;
+        }
+
+        canvas.RotateDegrees(angle, (float)ContentPosition.X, (float)ContentPosition.Y);
+        canvas.Scale(FlipX ? -1 : 1, FlipY ? -1 : 1, (float)ContentPosition.X, (float)ContentPosition.Y);
+        canvas.Translate((float)ContentPosition.X, (float)ContentPosition.Y);
+
         DrawCheckerboard(canvas, surfaceRectToRender);
 
         using Image snapshot = Surface.DrawingSurface.Snapshot(surfaceRectToRender);
@@ -548,7 +501,7 @@ internal class DrawSceneOperation : SkiaDrawOperation
         ShapeCorners surfaceInViewportSpace = SurfaceToViewport(new RectI(VecI.Zero, Surface.Size), finalScale);
         RectI surfaceBoundsInViewportSpace = (RectI)surfaceInViewportSpace.AABBBounds.RoundOutwards();
         RectI viewportBoundsInViewportSpace =
-            (RectI)(new RectD(Bounds.X, Bounds.Y, Bounds.Width, Bounds.Height)).RoundOutwards();
+            (RectI)(new RectD(ViewportBounds.X, ViewportBounds.Y, ViewportBounds.Width, ViewportBounds.Height)).RoundOutwards();
         RectI firstIntersectionInViewportSpace = surfaceBoundsInViewportSpace.Intersect(viewportBoundsInViewportSpace);
         ShapeCorners firstIntersectionInSurfaceSpace = ViewportToSurface(firstIntersectionInViewportSpace, finalScale);
         RectI firstIntersectionBoundsInSurfaceSpace = (RectI)firstIntersectionInSurfaceSpace.AABBBounds.RoundOutwards();

+ 2 - 2
src/PixiEditor.AvaloniaUI/Views/Visuals/SkiaDrawOperation.cs

@@ -10,9 +10,9 @@ internal abstract class SkiaDrawOperation : ICustomDrawOperation
 {
     public Rect Bounds { get; }
 
-    public SkiaDrawOperation(Rect bounds)
+    public SkiaDrawOperation(Rect dirtyBounds)
     {
-        Bounds = bounds;
+        Bounds = dirtyBounds;
     }
 
     public abstract bool Equals(ICustomDrawOperation? other);

+ 1 - 1
src/PixiEditor.AvaloniaUI/Views/Visuals/SurfaceControl.cs

@@ -138,7 +138,7 @@ internal class DrawSurfaceOperation : SkiaDrawOperation
 
     private SKPaint _paint = new SKPaint();
 
-    public DrawSurfaceOperation(Rect bounds, Surface surface, Stretch stretch, double opacity = 1) : base(bounds)
+    public DrawSurfaceOperation(Rect dirtyBounds, Surface surface, Stretch stretch, double opacity = 1) : base(dirtyBounds)
     {
         Surface = surface;
         Stretch = stretch;

+ 3 - 3
src/PixiEditor.Zoombox/Operations/MoveDragOperation.cs

@@ -18,14 +18,14 @@ internal class MoveDragOperation : IDragOperation
 
     public void Start(PointerEventArgs e)
     {
-        prevMousePos = Zoombox.ToVecD(e.GetPosition(parent.mainCanvas));
-        e.Pointer.Capture(parent.mainGrid);
+        prevMousePos = Zoombox.ToVecD(e.GetPosition(parent));
+        e.Pointer.Capture(parent);
         capturedPointer = e.Pointer;
     }
 
     public void Update(PointerEventArgs e)
     {
-        var curMousePos = Zoombox.ToVecD(e.GetPosition(parent.mainCanvas));
+        var curMousePos = Zoombox.ToVecD(e.GetPosition(parent));
         parent.Center += parent.ToZoomboxSpace(prevMousePos) - parent.ToZoomboxSpace(curMousePos);
         prevMousePos = curMousePos;
     }

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

@@ -22,17 +22,17 @@ internal class RotateDragOperation : IDragOperation
 
     public void Start(PointerEventArgs e)
     {
-        Point pointCur = e.GetPosition(owner.mainCanvas);
+        Point pointCur = e.GetPosition(owner);
         initialClickAngle = GetAngle(new(pointCur.X, pointCur.Y));
         initialZoomboxAngle = owner.Angle;
         rotationProcess = new LockingRotationProcess(initialZoomboxAngle);
-        e.Pointer.Capture(owner.mainGrid);
+        e.Pointer.Capture(owner);
         capturedPointer = e.Pointer;
     }
 
     private double GetAngle(VecD point)
     {
-        VecD center = new(owner.mainCanvas.Bounds.Width / 2, owner.mainCanvas.Bounds.Height / 2);
+        VecD center = new(owner.Bounds.Width / 2, owner.Bounds.Height / 2);
         double angle = (point - center).Angle;
         if (double.IsNaN(angle) || double.IsInfinity(angle))
             return 0;
@@ -41,7 +41,7 @@ internal class RotateDragOperation : IDragOperation
 
     public void Update(PointerEventArgs e)
     {
-        Point pointCur = e.GetPosition(owner.mainCanvas);
+        Point pointCur = e.GetPosition(owner);
         double clickAngle = GetAngle(new(pointCur.X, pointCur.Y));
         double newZoomboxAngle = initialZoomboxAngle;
         if (owner.FlipX ^ owner.FlipY)

+ 3 - 3
src/PixiEditor.Zoombox/Operations/ZoomDragOperation.cs

@@ -24,16 +24,16 @@ internal class ZoomDragOperation : IDragOperation
 
     public void Start(PointerEventArgs e)
     {
-        screenScaleOrigin = Zoombox.ToVecD(e.GetPosition(parent.mainCanvas));
+        screenScaleOrigin = Zoombox.ToVecD(e.GetPosition(parent));
         scaleOrigin = parent.ToZoomboxSpace(screenScaleOrigin);
         originalScale = parent.Scale;
         capturedPointer = e.Pointer;
-        e.Pointer.Capture(parent.mainGrid);
+        e.Pointer.Capture(parent);
     }
 
     public void Update(PointerEventArgs e)
     {
-        Point curScreenPos = e.GetPosition(parent.mainCanvas);
+        Point curScreenPos = e.GetPosition(parent);
         double deltaX = curScreenPos.X - screenScaleOrigin.X;
         double deltaPower = deltaX / 10.0;
 

+ 0 - 43
src/PixiEditor.Zoombox/Zoombox.axaml

@@ -1,43 +0,0 @@
-<UserControl
-    x:Class="PixiEditor.Zoombox.Zoombox"
-    xmlns="https://github.com/avaloniaui"
-    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:PixiEditor.Zoombox"
-    mc:Ignorable="d"
-    x:Name="uc"
-    d:DesignHeight="450"
-    PointerWheelChanged="OnScroll"
-    PointerPressed="OnMouseDown"
-    PointerReleased="OnMouseUp"
-    PointerMoved="OnMouseMove"
-    d:DesignWidth="800">
-    <Canvas
-        ClipToBounds="True"
-        x:Name="mainCanvas"
-        Background="Transparent"
-        SizeChanged="OnMainCanvasSizeChanged">
-        <Grid
-            RenderTransformOrigin="0,0"
-            x:Name="mainGrid"
-            SizeChanged="OnGridSizeChanged"
-            Canvas.Left="{Binding ElementName=uc, Path=CanvasX}"
-            Canvas.Top="{Binding ElementName=uc, Path=CanvasY}">
-            <Grid.RenderTransform>
-                <TransformGroup>
-                    <ScaleTransform
-                        ScaleX="{Binding ElementName=uc, Path=ScaleTransformXY}"
-                        ScaleY="{Binding ElementName=uc, Path=ScaleTransformXY}" />
-                    <RotateTransform
-                        Angle="{Binding ElementName=uc, Path=RotateTransformAngle}" />
-                    <ScaleTransform
-                        ScaleX="{Binding ElementName=uc, Path=FlipTransformX}"
-                        ScaleY="{Binding ElementName=uc, Path=FlipTransformY}" />
-                </TransformGroup>
-            </Grid.RenderTransform>
-            <ContentPresenter RenderTransformOrigin="0.5,0.5"
-                              Content="{Binding AdditionalContent, ElementName=uc}" />
-        </Grid>
-    </Canvas>
-</UserControl>

+ 32 - 32
src/PixiEditor.Zoombox/Zoombox.axaml.cs → src/PixiEditor.Zoombox/Zoombox.cs

@@ -12,7 +12,7 @@ using PixiEditor.Zoombox.Operations;
 
 namespace PixiEditor.Zoombox;
 
-public partial class Zoombox : UserControl, INotifyPropertyChanged //TODO: Make it content control
+public partial class Zoombox : ContentControl, INotifyPropertyChanged
 {
     public static readonly StyledProperty<ZoomboxMode> ZoomModeProperty =
         AvaloniaProperty.Register<Zoombox, ZoomboxMode>(nameof(ZoomMode), defaultValue: ZoomboxMode.Normal);
@@ -35,6 +35,16 @@ public partial class Zoombox : UserControl, INotifyPropertyChanged //TODO: Make
     public static readonly StyledProperty<VecD> RealDimensionsProperty =
         AvaloniaProperty.Register<Zoombox, VecD>(nameof(RealDimensions));
 
+    public static readonly StyledProperty<VecD> ContentDimensionsProperty = AvaloniaProperty.Register<Zoombox, VecD>(
+        nameof(ContentDimensions),
+        defaultValue: new VecD(0, 0));
+
+    public VecD ContentDimensions
+    {
+        get => GetValue(ContentDimensionsProperty);
+        set => SetValue(ContentDimensionsProperty, value);
+    }
+
     public static readonly StyledProperty<double> AngleProperty =
         AvaloniaProperty.Register<Zoombox, double>(nameof(Angle), defaultValue: 0.0);
 
@@ -44,9 +54,6 @@ public partial class Zoombox : UserControl, INotifyPropertyChanged //TODO: Make
     public static readonly StyledProperty<bool> FlipYProperty =
         AvaloniaProperty.Register<Zoombox, bool>(nameof(FlipY), defaultValue: false);
 
-    public static readonly StyledProperty<Control> AdditionalContentProperty =
-        AvaloniaProperty.Register<Zoombox, Control>(nameof(AdditionalContent));
-
     public static readonly RoutedEvent<ViewportRoutedEventArgs> ViewportMovedEvent = RoutedEvent.Register<Zoombox, ViewportRoutedEventArgs>(
         nameof(ViewportMoved), RoutingStrategies.Bubble);
 
@@ -110,12 +117,6 @@ public partial class Zoombox : UserControl, INotifyPropertyChanged //TODO: Make
         set => SetValue(RealDimensionsProperty, value);
     }
 
-    public Control AdditionalContent
-    {
-        get => (Control)GetValue(AdditionalContentProperty);
-        set => SetValue(AdditionalContentProperty, value);
-    }
-
     public event EventHandler<ViewportRoutedEventArgs> ViewportMoved
     {
         add => AddHandler(ViewportMovedEvent, value);
@@ -147,31 +148,28 @@ public partial class Zoombox : UserControl, INotifyPropertyChanged //TODO: Make
     {
         get
         {
-            double fraction = Math.Max(
-                mainCanvas.Bounds.Width / mainGrid.Bounds.Width,
-                mainCanvas.Bounds.Height / mainGrid.Bounds.Height);
+            double fraction = Math.Max(Bounds.Width / ContentDimensions.X, Bounds.Height / ContentDimensions.Y);
             return Math.Min(fraction / 8, 0.1);
         }
     }
 
     internal const double ScaleFactor = 1.09050773267; //2^(1/8)
 
-    internal VecD ToScreenSpace(VecD p)
+    public VecD ToScreenSpace(VecD p)
     {
-        if (mainCanvas == null) return p;
         VecD delta = p - Center;
         delta = delta.Rotate(Angle) * Scale;
         if (FlipX)
             delta.X = -delta.X;
         if (FlipY)
             delta.Y = -delta.Y;
-        delta += new VecD(mainCanvas.Bounds.Width / 2f, mainCanvas.Bounds.Height / 2f);
+        delta += new VecD(Bounds.Width / 2f, Bounds.Height / 2f);
         return delta;
     }
 
-    internal VecD ToZoomboxSpace(VecD mousePos)
+    public VecD ToZoomboxSpace(VecD mousePos)
     {
-        VecD delta = mousePos - new VecD(mainCanvas.Bounds.Width / 2, mainCanvas.Bounds.Height / 2);
+        VecD delta = mousePos - new VecD(Bounds.Width / 2, Bounds.Height / 2);
         if (FlipX)
             delta.X = -delta.X;
         if (FlipY)
@@ -215,7 +213,10 @@ public partial class Zoombox : UserControl, INotifyPropertyChanged //TODO: Make
     public Zoombox()
     {
         CalculateZoomValues();
-        InitializeComponent();
+        PointerWheelChanged += OnScroll;
+        PointerPressed += OnMouseDown;
+        PointerReleased += OnMouseUp;
+        PointerMoved += OnMouseMove;
         Loaded += (_, _) => OnPropertyChange(this);
     }
 
@@ -288,7 +289,7 @@ public partial class Zoombox : UserControl, INotifyPropertyChanged //TODO: Make
 
     private void RaiseViewportEvent()
     {
-        VecD realDim = new VecD(mainCanvas.Bounds.Width, mainCanvas.Bounds.Height);
+        VecD realDim = new VecD(Bounds.Width, Bounds.Height);
         RealDimensions = realDim;
         RaiseEvent(new ViewportRoutedEventArgs(
             ViewportMovedEvent,
@@ -298,14 +299,14 @@ public partial class Zoombox : UserControl, INotifyPropertyChanged //TODO: Make
             Angle));
     }
 
-    public void CenterContent() => CenterContent(new(mainGrid.Bounds.Width, mainGrid.Bounds.Height));
+    public void CenterContent() => CenterContent(new(ContentDimensions.X, ContentDimensions.Y));
 
     public void CenterContent(VecD newSize)
     {
         const double marginFactor = 1.1;
         double scaleFactor = Math.Max(
-            newSize.X * marginFactor / mainCanvas.Bounds.Width,
-            newSize.Y * marginFactor / mainCanvas.Bounds.Height);
+            newSize.X * marginFactor / Bounds.Width,
+            newSize.Y * marginFactor / Bounds.Height);
 
         Angle = 0;
         FlipX = false;
@@ -316,7 +317,7 @@ public partial class Zoombox : UserControl, INotifyPropertyChanged //TODO: Make
 
     public void ZoomIntoCenter(double delta)
     {
-        ZoomInto(new VecD(mainCanvas.Bounds.Width / 2, mainCanvas.Bounds.Height / 2), delta);
+        ZoomInto(new VecD(Bounds.Width / 2, Bounds.Height / 2), delta);
     }
 
     public void ZoomInto(VecD position, double delta)
@@ -358,7 +359,6 @@ public partial class Zoombox : UserControl, INotifyPropertyChanged //TODO: Make
 
     private void OnMouseDown(object? sender, PointerPressedEventArgs e)
     {
-        // TODO: idk if this is correct
         MouseButton but = e.GetCurrentPoint(this).Properties.PointerUpdateKind switch
         {
             PointerUpdateKind.LeftButtonPressed => MouseButton.Left,
@@ -370,8 +370,8 @@ public partial class Zoombox : UserControl, INotifyPropertyChanged //TODO: Make
         if (but == MouseButton.Right)
             return;
         activeMouseDownEventArgs = e;
-        activeMouseDownPos = ToVecD(e.GetPosition(mainCanvas));
-        Focus(NavigationMethod.Unspecified);
+        activeMouseDownPos = ToVecD(e.GetPosition(this));
+        Focus();
     }
 
     private void InitiateDrag(PointerEventArgs e)
@@ -405,7 +405,7 @@ public partial class Zoombox : UserControl, INotifyPropertyChanged //TODO: Make
         else
         {
             if (ZoomMode == ZoomboxMode.Zoom && e.InitialPressMouseButton == MouseButton.Left)
-                ZoomInto(ToVecD(e.GetPosition(mainCanvas)), ZoomOutOnClick ? -1 : 1);
+                ZoomInto(ToVecD(e.GetPosition(this)), ZoomOutOnClick ? -1 : 1);
         }
         activeMouseDownEventArgs = null;
     }
@@ -414,7 +414,7 @@ public partial class Zoombox : UserControl, INotifyPropertyChanged //TODO: Make
     {
         if (activeDragOperation is null && activeMouseDownEventArgs is not null)
         {
-            VecD cur = ToVecD(e.GetPosition(mainCanvas));
+            VecD cur = ToVecD(e.GetPosition(this));
 
             if ((cur - activeMouseDownPos).TaxicabLength > 3)
                 InitiateDrag(activeMouseDownEventArgs);
@@ -422,12 +422,12 @@ public partial class Zoombox : UserControl, INotifyPropertyChanged //TODO: Make
         activeDragOperation?.Update(e);
     }
 
-    private void OnScroll(object sender, PointerWheelEventArgs e)
+    private void OnScroll(object? sender, PointerWheelEventArgs e)
     {
         double abs = Math.Abs(e.Delta.Y / 100.0);
         for (int i = 0; i < abs; i++)
         {
-            ZoomInto(ToVecD(e.GetPosition(mainCanvas)), e.Delta.Y / 100.0);
+            ZoomInto(ToVecD(e.GetPosition(this)), e.Delta.Y / 100.0);
         }
     }
 
@@ -469,7 +469,7 @@ public partial class Zoombox : UserControl, INotifyPropertyChanged //TODO: Make
     private static void OnPropertyChange(Zoombox zoombox)
     {
         VecD topLeft = zoombox.ToZoomboxSpace(VecD.Zero).Rotate(zoombox.Angle);
-        VecD bottomRight = zoombox.ToZoomboxSpace(new(zoombox.mainCanvas.Bounds.Width, zoombox.mainCanvas.Bounds.Height)).Rotate(zoombox.Angle);
+        VecD bottomRight = zoombox.ToZoomboxSpace(new(zoombox.Bounds.Width, zoombox.Bounds.Height)).Rotate(zoombox.Angle);
 
         zoombox.Dimensions = (bottomRight - topLeft).Abs();
         zoombox.PropertyChanged?.Invoke(zoombox, new(nameof(zoombox.ScaleTransformXY)));