Browse Source

A weird highlight

Equbuxu 3 years ago
parent
commit
b7c3797615

+ 10 - 0
src/PixiEditor/ViewModels/MainVM.cs

@@ -0,0 +1,10 @@
+using System.Windows.Markup;
+
+namespace PixiEditor.ViewModels;
+internal class MainVM : MarkupExtension
+{
+    public override object ProvideValue(IServiceProvider serviceProvider)
+    {
+        return ViewModelMain.Current;
+    }
+}

+ 31 - 21
src/PixiEditor/ViewModels/SubViewModels/Main/ToolsViewModel.cs

@@ -10,33 +10,42 @@ using PixiEditor.ViewModels.SubViewModels.Tools.Tools;
 using PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Toolbars;
 
 namespace PixiEditor.ViewModels.SubViewModels.Main;
-
+#nullable enable
 [Command.Group("PixiEditor.Tools", "Tools")]
 internal class ToolsViewModel : SubViewModel<ViewModelMain>
 {
-    public ZoomToolViewModel ZoomTool => (ZoomToolViewModel)GetTool<ZoomToolViewModel>();
+    public ZoomToolViewModel? ZoomTool => GetTool<ZoomToolViewModel>();
 
-    public ToolViewModel LastActionTool { get; private set; }
+    public ToolViewModel? LastActionTool { get; private set; }
 
     public bool ActiveToolIsTransient { get; set; }
 
-    private Cursor toolCursor;
-    public Cursor ToolCursor
+    private Cursor? toolCursor;
+    public Cursor? ToolCursor
     {
         get => toolCursor;
         set => SetProperty(ref toolCursor, value);
     }
 
-    private ToolViewModel activeTool;
-    public ToolViewModel ActiveTool
+    public BasicToolbar? ActiveBasicToolbar
+    {
+        get => ActiveTool?.Toolbar as BasicToolbar;
+    }
+
+    private ToolViewModel? activeTool;
+    public ToolViewModel? ActiveTool
     {
         get => activeTool;
-        private set => SetProperty(ref activeTool, value);
+        private set
+        {
+            SetProperty(ref activeTool, value);
+            RaisePropertyChanged(nameof(ActiveBasicToolbar));
+        }
     }
 
-    public List<ToolViewModel> ToolSet { get; private set; }
+    public List<ToolViewModel>? ToolSet { get; private set; }
 
-    public event EventHandler<SelectedToolEventArgs> SelectedToolChanged;
+    public event EventHandler<SelectedToolEventArgs>? SelectedToolChanged;
 
     private bool shiftIsDown;
     private bool ctrlIsDown;
@@ -54,7 +63,7 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>
 
     public void SetupToolsTooltipShortcuts(IServiceProvider services)
     {
-        foreach (ToolViewModel tool in ToolSet)
+        foreach (ToolViewModel tool in ToolSet!)
         {
             tool.Shortcut = Owner.ShortcutController.GetToolShortcut(tool.GetType());
         }
@@ -63,7 +72,7 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>
     public T? GetTool<T>()
         where T : ToolViewModel
     {
-        return (T)ToolSet?.Where(static tool => tool is T).FirstOrDefault();
+        return (T?)ToolSet?.Where(static tool => tool is T).FirstOrDefault();
     }
 
     public void SetActiveTool<T>()
@@ -75,7 +84,7 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>
     [Command.Basic("PixiEditor.Tools.ApplyTransform", "Apply transform", "", Key = Key.Enter)]
     public void ApplyTransform()
     {
-        DocumentViewModel doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
+        DocumentViewModel? doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
         if (doc is null)
             return;
         doc.EventInlet.OnApplyTransform();
@@ -91,9 +100,9 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>
 
         ActiveToolIsTransient = false;
         bool shareToolbar = IPreferences.Current.GetPreference<bool>("EnableSharedToolbar");
-        if (ActiveTool != null)
+        if (ActiveTool is not null)
         {
-            activeTool.IsActive = false;
+            ActiveTool.IsActive = false;
             if (shareToolbar)
                 ActiveTool.Toolbar.SaveToolbarSettings();
         }
@@ -148,16 +157,17 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>
 
     public void SetActiveTool(Type toolType)
     {
-        if (!typeof(ToolViewModel).IsAssignableFrom(toolType)) { throw new ArgumentException($"'{toolType}' does not inherit from {typeof(ToolViewModel)}"); }
-        ToolViewModel foundTool = ToolSet.First(x => x.GetType() == toolType);
+        if (!typeof(ToolViewModel).IsAssignableFrom(toolType))
+            throw new ArgumentException($"'{toolType}' does not inherit from {typeof(ToolViewModel)}");
+        ToolViewModel foundTool = ToolSet!.First(x => x.GetType() == toolType);
         SetActiveTool(foundTool);
     }
 
     private void SetToolCursor(Type tool)
     {
-        if (tool != null)
+        if (tool is not null)
         {
-            ToolCursor = ActiveTool.Cursor;
+            ToolCursor = ActiveTool?.Cursor;
         }
         else
         {
@@ -181,7 +191,7 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>
         this.ctrlIsDown |= ctrlIsDown;
         this.altIsDown |= altIsDown;
 
-        ActiveTool.UpdateActionDisplay(this.ctrlIsDown, this.shiftIsDown, this.altIsDown);
+        ActiveTool?.UpdateActionDisplay(this.ctrlIsDown, this.shiftIsDown, this.altIsDown);
     }
 
     public void OnKeyUp(Key key)
@@ -197,6 +207,6 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>
             this.ctrlIsDown = false;
         if (altIsUp)
             this.altIsDown = false;
-        ActiveTool.UpdateActionDisplay(ctrlIsDown, shiftIsDown, altIsDown);
+        ActiveTool?.UpdateActionDisplay(ctrlIsDown, shiftIsDown, altIsDown);
     }
 }

+ 4 - 1
src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Toolbars/BasicToolbar.cs

@@ -14,6 +14,9 @@ internal class BasicToolbar : Toolbar
     }
     public BasicToolbar()
     {
-        Settings.Add(new SizeSetting(nameof(ToolSize), "Tool size:"));
+        var setting = new SizeSetting(nameof(ToolSize), "Tool size:");
+        setting.ValueChanged += (_, _) => RaisePropertyChanged(nameof(ToolSize));
+        Settings.Add(setting);
+
     }
 }

+ 1 - 1
src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Toolbars/Toolbar.cs

@@ -3,7 +3,7 @@ using PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Settings;
 
 namespace PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Toolbars;
 
-internal abstract class Toolbar
+internal abstract class Toolbar : NotifyableObject
 {
     private static readonly List<Setting> SharedSettings = new List<Setting>();
 

+ 6 - 0
src/PixiEditor/Views/UserControls/BrushShapeOverlay/BrushShape.cs

@@ -0,0 +1,6 @@
+namespace PixiEditor.Views.UserControls.BrushShapeOverlay;
+internal enum BrushShape
+{
+    Square,
+    Circle
+}

+ 124 - 0
src/PixiEditor/Views/UserControls/BrushShapeOverlay/BrushShapeOverlay.cs

@@ -0,0 +1,124 @@
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using ChunkyImageLib.DataHolders;
+using ChunkyImageLib.Operations;
+
+namespace PixiEditor.Views.UserControls.BrushShapeOverlay;
+#nullable enable
+internal class BrushShapeOverlay : Control
+{
+    public static readonly DependencyProperty ZoomboxScaleProperty =
+        DependencyProperty.Register(nameof(ZoomboxScale), typeof(double), typeof(BrushShapeOverlay), new(1.0, OnZoomboxScaleChanged));
+
+    public static readonly DependencyProperty BrushSizeProperty =
+        DependencyProperty.Register(nameof(BrushSize), typeof(int), typeof(BrushShapeOverlay),
+            new FrameworkPropertyMetadata(1, FrameworkPropertyMetadataOptions.AffectsRender));
+
+    public static readonly DependencyProperty MouseEventSourceProperty =
+        DependencyProperty.Register(nameof(MouseEventSource), typeof(UIElement), typeof(BrushShapeOverlay), new(null));
+
+    public static readonly DependencyProperty MouseReferenceProperty =
+        DependencyProperty.Register(nameof(MouseReference), typeof(UIElement), typeof(BrushShapeOverlay), new(null));
+
+    public UIElement? MouseReference
+    {
+        get => (UIElement?)GetValue(MouseReferenceProperty);
+        set => SetValue(MouseReferenceProperty, value);
+    }
+
+    public UIElement? MouseEventSource
+    {
+        get => (UIElement?)GetValue(MouseEventSourceProperty);
+        set => SetValue(MouseEventSourceProperty, value);
+    }
+
+    public int BrushSize
+    {
+        get => (int)GetValue(BrushSizeProperty);
+        set => SetValue(BrushSizeProperty, value);
+    }
+
+    public double ZoomboxScale
+    {
+        get => (double)GetValue(ZoomboxScaleProperty);
+        set => SetValue(ZoomboxScaleProperty, value);
+    }
+
+    private Pen whitePen = new Pen(Brushes.White, 1);
+    private Pen blackPen = new Pen(Brushes.Black, 1);
+    private Point lastMousePos = new();
+
+    public BrushShapeOverlay()
+    {
+        Loaded += ControlLoaded;
+        Unloaded += ControlUnloaded;
+    }
+
+    private void ControlUnloaded(object sender, RoutedEventArgs e)
+    {
+        if (MouseEventSource is null)
+            return;
+        MouseEventSource.MouseMove -= SourceMouseMove;
+    }
+
+    private void ControlLoaded(object sender, RoutedEventArgs e)
+    {
+        if (MouseEventSource is null)
+            return;
+        MouseEventSource.MouseMove += SourceMouseMove;
+    }
+
+    private void SourceMouseMove(object sender, MouseEventArgs args)
+    {
+        if (MouseReference is null)
+            return;
+        lastMousePos = args.GetPosition(MouseReference);
+        InvalidateVisual();
+    }
+
+    protected override void OnRender(DrawingContext drawingContext)
+    {
+        var winRect = new Rect(
+            (Point)(new Point(Math.Floor(lastMousePos.X), Math.Floor(lastMousePos.Y)) - new Point(BrushSize / 2, BrushSize / 2)),
+            new Size(BrushSize, BrushSize)
+            );
+        var rectI = new RectI((int)winRect.X, (int)winRect.Y, (int)winRect.Width, (int)winRect.Height);
+
+        if (BrushSize < 3)
+        {
+            drawingContext.DrawRectangle(null, blackPen, winRect);
+        }
+        else
+        {
+            var geometry = ConstructEllipseOutline(rectI);
+            drawingContext.DrawGeometry(null, whitePen, geometry);
+        }
+        //drawingContext.DrawRectangle(null, whitePen, winRect.inf);
+    }
+
+    private static PathGeometry ConstructEllipseOutline(RectI rectangle)
+    {
+        var points = EllipseHelper.GenerateEllipseFromRect(rectangle);
+        var center = rectangle.Center;
+        points.Sort((vec, vec2) => Math.Sign((vec - center).Angle - (vec2 - center).Angle));
+        PathFigure figure = new PathFigure()
+        {
+            StartPoint = new Point(points[0].X, points[0].Y),
+            Segments = new PathSegmentCollection(points.Select(static point => new LineSegment(new Point(point.X, point.Y), true))),
+            IsClosed = true
+        };
+
+        var geometry = new PathGeometry(new PathFigure[] { figure });
+        return geometry;
+    }
+
+    private static void OnZoomboxScaleChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
+    {
+        var self = (BrushShapeOverlay)obj;
+        double newScale = (double)args.NewValue;
+        self.whitePen.Thickness = 1.0 / newScale;
+        self.blackPen.Thickness = 1.0 / newScale;
+    }
+}

+ 11 - 0
src/PixiEditor/Views/UserControls/Viewport.xaml

@@ -11,7 +11,9 @@
     xmlns:to="clr-namespace:PixiEditor.Views.UserControls.TransformOverlay"
     xmlns:uc="clr-namespace:PixiEditor.Views.UserControls"
     xmlns:sym="clr-namespace:PixiEditor.Views.UserControls.SymmetryOverlay"
+    xmlns:brush="clr-namespace:PixiEditor.Views.UserControls.BrushShapeOverlay"
     xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
+    xmlns:vm="clr-namespace:PixiEditor.ViewModels"
     xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
     xmlns:cmds="clr-namespace:PixiEditor.Models.Commands.XAML"
     mc:Ignorable="d"
@@ -19,6 +21,7 @@
     d:DesignHeight="450"
     d:DesignWidth="800">
     <Grid 
+        x:Name="mainGrid"
         MouseDown="Image_MouseDown"
         MouseMove="Image_MouseMove"
         MouseUp="Image_MouseUp"
@@ -42,6 +45,7 @@
             </i:EventTrigger>
         </i:Interaction.Triggers>
         <zoombox:Zoombox
+            Tag="{Binding ElementName=vpUc}"
             x:Name="zoombox"
             UseTouchGestures="{Binding UseTouchGestures, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}, Mode=OneWayToSource}"
             Scale="{Binding ZoomboxScale, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}, Mode=OneWayToSource}"
@@ -105,6 +109,13 @@
                     <uc:SelectionOverlay
                         Path="{Binding Document.SelectionPathBindable}"
                         ZoomboxScale="{Binding Zoombox.Scale}" />
+                    <brush:BrushShapeOverlay
+                        IsHitTestVisible="False"
+                        ZoomboxScale="{Binding Zoombox.Scale}"
+                        MouseEventSource="{Binding Zoombox.Tag.BackgroundGrid, Mode=OneTime}"
+                        MouseReference="{Binding Zoombox.Tag.MainImage, Mode=OneTime}"
+                        BrushSize="{Binding ToolsSubViewModel.ActiveBasicToolbar.ToolSize, Source={vm:MainVM}}"
+                        />
                     <to:TransformOverlay
                         Cursor="Arrow"
                         IsHitTestVisible="{Binding ZoomMode, Converter={converters:ZoomModeToHitTestVisibleConverter}}"

+ 6 - 5
src/PixiEditor/Views/UserControls/Viewport.xaml.cs

@@ -288,16 +288,17 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
         Binding binding = new Binding { Source = this, Path = new PropertyPath("Document.Bitmaps") };
         SetBinding(BitmapsProperty, binding);
 
-        GetImage()!.Loaded += OnImageLoaded;
+        MainImage!.Loaded += OnImageLoaded;
         Loaded += OnLoad;
         Unloaded += OnUnload;
     }
 
-    private Image? GetImage() => (Image?)((Grid?)((Border?)zoombox.AdditionalContent)?.Child)?.Children[1];
+    public Image? MainImage => (Image?)((Grid?)((Border?)zoombox.AdditionalContent)?.Child)?.Children[1];
+    public Grid BackgroundGrid => mainGrid;
 
     private void ForceRefreshFinalImage()
     {
-        GetImage()?.InvalidateVisual();
+        MainImage?.InvalidateVisual();
     }
 
     private void OnUnload(object sender, RoutedEventArgs e)
@@ -351,7 +352,7 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
     {
         if (MouseDownCommand is null)
             return;
-        Point pos = e.GetPosition(GetImage());
+        Point pos = e.GetPosition(MainImage);
         VecD conv = new VecD(pos.X, pos.Y);
         MouseOnCanvasEventArgs? parameter = new MouseOnCanvasEventArgs(e.ChangedButton, conv);
 
@@ -363,7 +364,7 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
     {
         if (MouseMoveCommand is null)
             return;
-        Point pos = e.GetPosition(GetImage());
+        Point pos = e.GetPosition(MainImage);
         VecD conv = new VecD(pos.X, pos.Y);
 
         if (MouseMoveCommand.CanExecute(conv))

+ 1 - 1
src/PixiEditorPrototype/CustomControls/SelectionOverlay.cs

@@ -13,7 +13,6 @@ internal class SelectionOverlay : Control
         DependencyProperty.Register(nameof(Path), typeof(SKPath), typeof(SelectionOverlay),
             new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
 
-
     public static readonly DependencyProperty ZoomboxScaleProperty =
         DependencyProperty.Register(nameof(ZoomboxScale), typeof(double), typeof(SelectionOverlay), new(1.0, OnZoomboxScaleChanged));
 
@@ -63,6 +62,7 @@ internal class SelectionOverlay : Control
             RepeatBehavior = RepeatBehavior.Forever,
             Duration = new Duration(new TimeSpan(0, 0, 0, 0, 500))
         });
+
     }
 
     protected override void OnRender(DrawingContext drawingContext)

+ 28 - 28
src/PixiEditorPrototype/ViewModels/ViewModelMain.cs

@@ -49,7 +49,7 @@ internal class ViewModelMain : INotifyPropertyChanged
             PropertyChanged?.Invoke(this, new(nameof(SelectedColor)));
         }
     }
-    
+
     public bool NormalZoombox
     {
         set
@@ -215,35 +215,35 @@ internal class ViewModelMain : INotifyPropertyChanged
         switch (toolOnMouseDown)
         {
             case Tool.Rectangle:
-            {
-                var rect = RectI.FromTwoPixels((VecI)mouseDownCanvasPos, (VecI)canvasPos);
-                ActiveDocument!.StartUpdateRectangle(new ShapeData(
-                    rect.Center,
-                    rect.Size,
-                    0,
-                    (int)StrokeWidth,
-                    new SKColor(SelectedColor.R, SelectedColor.G, SelectedColor.B, SelectedColor.A),
-                    new SKColor(0, 0, 255, 128)));
-                break;
-            }
+                {
+                    var rect = RectI.FromTwoPixels((VecI)mouseDownCanvasPos, (VecI)canvasPos);
+                    ActiveDocument!.StartUpdateRectangle(new ShapeData(
+                        rect.Center,
+                        rect.Size,
+                        0,
+                        (int)StrokeWidth,
+                        new SKColor(SelectedColor.R, SelectedColor.G, SelectedColor.B, SelectedColor.A),
+                        new SKColor(0, 0, 255, 128)));
+                    break;
+                }
             case Tool.Ellipse:
-            {
-                ActiveDocument!.StartUpdateEllipse(
-                    RectI.FromTwoPixels((VecI)mouseDownCanvasPos, (VecI)canvasPos),
-                    new SKColor(SelectedColor.R, SelectedColor.G, SelectedColor.B, SelectedColor.A),
-                    new SKColor(0, 0, 255, 128),
-                    (int)StrokeWidth);
-                break;
-            }
+                {
+                    ActiveDocument!.StartUpdateEllipse(
+                        RectI.FromTwoPixels((VecI)mouseDownCanvasPos, (VecI)canvasPos),
+                        new SKColor(SelectedColor.R, SelectedColor.G, SelectedColor.B, SelectedColor.A),
+                        new SKColor(0, 0, 255, 128),
+                        (int)StrokeWidth);
+                    break;
+                }
             case Tool.Line:
-            {
-                ActiveDocument!.StartUpdateLine(
-                    (VecI)mouseDownCanvasPos, (VecI)canvasPos,
-                    new SKColor(SelectedColor.R, SelectedColor.G, SelectedColor.B, SelectedColor.A),
-                    LineStrokeCap,
-                    (int)StrokeWidth);
-                break;
-            }
+                {
+                    ActiveDocument!.StartUpdateLine(
+                        (VecI)mouseDownCanvasPos, (VecI)canvasPos,
+                        new SKColor(SelectedColor.R, SelectedColor.G, SelectedColor.B, SelectedColor.A),
+                        LineStrokeCap,
+                        (int)StrokeWidth);
+                    break;
+                }
             case Tool.SelectRectangle:
                 ActiveDocument!.StartUpdateRectSelection(
                     RectI.FromTwoPixels((VecI)mouseDownCanvasPos, (VecI)canvasPos),