This commit is contained in:
Wiesław Šoltés 2023-12-31 17:50:06 +01:00
Родитель 996684464f
Коммит 43b7376466
10 изменённых файлов: 185 добавлений и 14 удалений

Просмотреть файл

@ -0,0 +1,111 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
namespace VectorPaint.Controls;
public sealed class GridLines : Control
{
public static readonly StyledProperty<int> CellWidthProperty =
AvaloniaProperty.Register<GridLines, int>(nameof(CellWidth), 10);
public static readonly StyledProperty<int> CellHeightProperty =
AvaloniaProperty.Register<GridLines, int>(nameof(CellHeight), 10);
public static readonly StyledProperty<int> BoldSeparatorHorizontalSpacingProperty =
AvaloniaProperty.Register<GridLines, int>(nameof(BoldSeparatorHorizontalSpacing), 10);
public static readonly StyledProperty<int> BoldSeparatorVerticalSpacingProperty =
AvaloniaProperty.Register<GridLines, int>(nameof(BoldSeparatorVerticalSpacing), 10);
public static readonly StyledProperty<bool> IsGridEnabledProperty =
AvaloniaProperty.Register<GridLines, bool>(nameof(IsGridEnabled), true);
private readonly Pen _pen;
private readonly Pen _penBold;
public int CellWidth
{
get => GetValue(CellWidthProperty);
set => SetValue(CellWidthProperty, value);
}
public int CellHeight
{
get => GetValue(CellHeightProperty);
set => SetValue(CellHeightProperty, value);
}
public int BoldSeparatorHorizontalSpacing
{
get => GetValue(BoldSeparatorHorizontalSpacingProperty);
set => SetValue(BoldSeparatorHorizontalSpacingProperty, value);
}
public int BoldSeparatorVerticalSpacing
{
get => GetValue(BoldSeparatorVerticalSpacingProperty);
set => SetValue(BoldSeparatorVerticalSpacingProperty, value);
}
public bool IsGridEnabled
{
get => GetValue(IsGridEnabledProperty);
set => SetValue(IsGridEnabledProperty, value);
}
public GridLines()
{
_pen = new Pen(new SolidColorBrush(Color.FromArgb((byte)(255.0 * 0.1), 14, 94, 253)));
_penBold = new Pen(new SolidColorBrush(Color.FromArgb((byte)(255.0 * 0.3), 14, 94, 253)));
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == CellWidthProperty
|| change.Property == CellHeightProperty
|| change.Property == BoldSeparatorHorizontalSpacingProperty
|| change.Property == BoldSeparatorVerticalSpacingProperty
|| change.Property == IsGridEnabledProperty)
{
InvalidateVisual();
}
}
public override void Render(DrawingContext context)
{
base.Render(context);
if (!IsGridEnabled)
{
return;
}
var cellWidth = CellWidth;
var cellHeight = CellHeight;
var boldSeparatorHorizontalSpacing = BoldSeparatorHorizontalSpacing;
var boldSeparatorVerticalSpacing = BoldSeparatorVerticalSpacing;
var width = Bounds.Width;
var height = Bounds.Height;
for(var i = 1; i < height / cellHeight; i++)
{
var pen = i % boldSeparatorVerticalSpacing == 0 ? _penBold : _pen;
context.DrawLine(
pen,
new Point(0 + 0.5, i * cellHeight + 0.5),
new Point(width + 0.5, i * cellHeight + 0.5));
}
for (var i = 1; i < width / cellWidth; i++)
{
var pen = i % boldSeparatorHorizontalSpacing == 0 ? _penBold : _pen;
context.DrawLine(
pen,
new Point(i * cellWidth + 0.5, 0 + 0.5),
new Point(i * cellWidth + 0.5, height + 0.5));
}
}
}

Просмотреть файл

@ -72,7 +72,8 @@ public class Drawing : ReactiveObject, IDrawing
public void Draw(DrawingContext context, Rect bounds)
{
context.DrawRectangle(Brushes.WhiteSmoke, null, bounds);
// context.DrawRectangle(Brushes.WhiteSmoke, null, bounds);
context.DrawRectangle(Brushes.Transparent, null, bounds);
if (_drawables is { })
{

Просмотреть файл

@ -0,0 +1,29 @@
using Avalonia;
namespace VectorPaint.ViewModels.Core;
public static class SnapHelper
{
public static double SnapValue(double value, double snap)
{
if (snap == 0.0)
{
return value;
}
var c = value % snap;
var r = c >= snap / 2.0 ? value + snap - c : value - c;
return r;
}
public static Point SnapPoint(Point point, double snapX = 5, double snapY = 5, bool enabled = true)
{
if (enabled)
{
var pointX = SnapValue(point.X, snapX);
var pointY = SnapValue(point.Y, snapY);
return new Point(pointX, pointY);
}
return point;
}
}

Просмотреть файл

@ -1,4 +1,5 @@
using Avalonia.Input;
using VectorPaint.ViewModels.Core;
using VectorPaint.ViewModels.Drawables;
namespace VectorPaint.ViewModels.Tools;
@ -17,6 +18,8 @@ public class EllipseTool : Tool
}
var point = e.GetCurrentPoint(drawing.Input).Position;
point = SnapHelper.SnapPoint(point);
_ellipse = new EllipseDrawable()
{
@ -57,6 +60,8 @@ public class EllipseTool : Tool
if (_ellipse?.BottomRight is { })
{
var point = e.GetCurrentPoint(drawing.Input).Position;
point = SnapHelper.SnapPoint(point);
_ellipse.BottomRight.X = point.X;
_ellipse.BottomRight.Y = point.Y;

Просмотреть файл

@ -1,4 +1,5 @@
using Avalonia.Input;
using VectorPaint.ViewModels.Core;
using VectorPaint.ViewModels.Drawables;
namespace VectorPaint.ViewModels.Tools;
@ -18,6 +19,8 @@ public class LineTool : Tool
var point = e.GetCurrentPoint(drawing.Input).Position;
point = SnapHelper.SnapPoint(point);
_line = new LineDrawable()
{
Fill = null,
@ -57,7 +60,9 @@ public class LineTool : Tool
if (_line?.End is { })
{
var point = e.GetCurrentPoint(drawing.Input).Position;
point = SnapHelper.SnapPoint(point);
_line.End.X = point.X;
_line.End.Y = point.Y;
_line.Invalidate();

Просмотреть файл

@ -1,4 +1,5 @@
using Avalonia.Input;
using VectorPaint.ViewModels.Core;
using VectorPaint.ViewModels.Drawables;
namespace VectorPaint.ViewModels.Tools;
@ -17,6 +18,8 @@ public class RectangleTool : Tool
}
var point = e.GetCurrentPoint(drawing.Input).Position;
point = SnapHelper.SnapPoint(point);
_rectangle = new RectangleDrawable()
{
@ -58,6 +61,8 @@ public class RectangleTool : Tool
{
var point = e.GetCurrentPoint(drawing.Input).Position;
point = SnapHelper.SnapPoint(point);
_rectangle.BottomRight.X = point.X;
_rectangle.BottomRight.Y = point.Y;
_rectangle.Invalidate();

Просмотреть файл

@ -4,6 +4,7 @@ using Avalonia;
using Avalonia.Input;
using Avalonia.Media;
using Avalonia.Media.Immutable;
using VectorPaint.ViewModels.Core;
using VectorPaint.ViewModels.Drawables;
namespace VectorPaint.ViewModels.Tools;
@ -79,7 +80,7 @@ public class SelectionTool : Tool
if (!_moving)
{
var point = e.GetCurrentPoint(drawing.Input).Position;
var rect = new Rect(_start, point);
var selected = drawing.HitTest(rect).ToList();
@ -110,13 +111,15 @@ public class SelectionTool : Tool
}
var point = e.GetCurrentPoint(drawing.Input).Position;
if (_moving)
{
if (_selected.Count > 0)
{
foreach (var drawable in _selected)
{
point = SnapHelper.SnapPoint(point);
drawable.Move(point - _start);
}

Просмотреть файл

@ -18,8 +18,8 @@ public class MainWindowViewModel : ViewModelBase
{
Drawing = new Drawing
{
DefaultFill = new ImmutableSolidColorBrush(Colors.Yellow),
DefaultStroke = new ImmutablePen(new ImmutableSolidColorBrush(Colors.Red), 2, null, PenLineCap.Round),
DefaultFill = new ImmutableSolidColorBrush(Colors.Transparent),
DefaultStroke = new ImmutablePen(new ImmutableSolidColorBrush(Colors.Black), 10, null, PenLineCap.Round),
Drawables = new ObservableCollection<Drawable>(),
OverlayDrawables = new ObservableCollection<Drawable>()
};
@ -37,7 +37,7 @@ public class MainWindowViewModel : ViewModelBase
Editor.CurrentTool = Editor.Tools[0];
Demo(Drawing);
// Demo(Drawing);
}
private void Demo(IDrawing drawing)

Просмотреть файл

@ -30,12 +30,24 @@
<Button Content="NonZero" Command="{Binding GroupNonZeroCommand}" />
</DockPanel>
<c:VectorCanvas Name="VectorCanvas"
Drawing="{Binding Drawing}"
Tools="{Binding Editor.Tools}"
CurrentTool="{Binding Editor.CurrentTool}"
ClipToBounds="True" />
<Border BoxShadow="4 11 30 1 #3F000000"
Background="White"
Width="600"
Height="600"
ZIndex="-1">
<Panel>
<c:GridLines CellWidth="10"
CellHeight="10"
BoldSeparatorHorizontalSpacing="10"
BoldSeparatorVerticalSpacing="10"
IsGridEnabled="True" />
<c:VectorCanvas Name="VectorCanvas"
Drawing="{Binding Drawing}"
Tools="{Binding Editor.Tools}"
CurrentTool="{Binding Editor.CurrentTool}"
ClipToBounds="True" />
</Panel>
</Border>
</DockPanel>
</UserControl>

Просмотреть файл

@ -8,6 +8,6 @@
Icon="/Assets/avalonia-logo.ico"
Title="VectorPaint"
WindowStartupLocation="CenterScreen"
Width="800" Height="600">
Width="1000" Height="800">
<views:MainView />
</Window>