diff --git a/UI/Controls/Ink/DrawingAttributes.cs b/UI/Controls/Ink/DrawingAttributes.cs new file mode 100644 index 00000000..aaca6c48 --- /dev/null +++ b/UI/Controls/Ink/DrawingAttributes.cs @@ -0,0 +1,11 @@ +using System.Windows.Media; + +namespace InkkSlinger.UI.Controls.Ink +{ + public class DrawingAttributes + { + public Color Color { get; set; } = Colors.Black; + public double Width { get; set; } = 2.0; + public double Height { get; set; } = 2.0; + } +} diff --git a/UI/Controls/Ink/InkCanvas.cs b/UI/Controls/Ink/InkCanvas.cs new file mode 100644 index 00000000..9daae38a --- /dev/null +++ b/UI/Controls/Ink/InkCanvas.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; + +namespace InkkSlinger.UI.Controls.Ink +{ + public class InkCanvas : FrameworkElement + { + public static readonly DependencyProperty StrokesProperty = + DependencyProperty.Register(nameof(Strokes), typeof(StrokeCollection), typeof(InkCanvas), + new FrameworkPropertyMetadata(new StrokeCollection(), FrameworkPropertyMetadataOptions.AffectsRender)); + + public static readonly DependencyProperty DefaultDrawingAttributesProperty = + DependencyProperty.Register(nameof(DefaultDrawingAttributes), typeof(DrawingAttributes), typeof(InkCanvas), + new FrameworkPropertyMetadata(new DrawingAttributes(), FrameworkPropertyMetadataOptions.AffectsRender)); + + public StrokeCollection Strokes + { + get => (StrokeCollection)GetValue(StrokesProperty); + set => SetValue(StrokesProperty, value); + } + + public DrawingAttributes DefaultDrawingAttributes + { + get => (DrawingAttributes)GetValue(DefaultDrawingAttributesProperty); + set => SetValue(DefaultDrawingAttributesProperty, value); + } + + private StylusPointCollection _currentPoints; + private bool _isDrawing; + + protected override void OnRender(DrawingContext dc) + { + base.OnRender(dc); + foreach (var stroke in Strokes) + { + var geo = stroke.GetGeometry(); + if (geo != Geometry.Empty) + dc.DrawGeometry(null, new Pen(new SolidColorBrush(stroke.DrawingAttributes.Color), stroke.DrawingAttributes.Width), geo); + } + } + + protected override void OnMouseDown(MouseButtonEventArgs e) + { + base.OnMouseDown(e); + _isDrawing = true; + var p = e.GetPosition(this); + _currentPoints = new StylusPointCollection { new StylusPoint(p.X, p.Y) }; + CaptureMouse(); + } + + protected override void OnMouseMove(MouseEventArgs e) + { + if (!_isDrawing) return; + var p = e.GetPosition(this); + _currentPoints.Add(new StylusPoint(p.X, p.Y)); + InvalidateVisual(); + } + + protected override void OnMouseUp(MouseButtonEventArgs e) + { + if (!_isDrawing) return; + var p = e.GetPosition(this); + _currentPoints.Add(new StylusPoint(p.X, p.Y)); + Strokes.Add(new Stroke(_currentPoints, DefaultDrawingAttributes)); + _isDrawing = false; + ReleaseMouseCapture(); + InvalidateVisual(); + } + } +} diff --git a/UI/Controls/Ink/InkPresenter.cs b/UI/Controls/Ink/InkPresenter.cs new file mode 100644 index 00000000..dc317393 --- /dev/null +++ b/UI/Controls/Ink/InkPresenter.cs @@ -0,0 +1,30 @@ +using System.Windows; +using System.Windows.Media; + +namespace InkkSlinger.UI.Controls.Ink +{ + public class InkPresenter : FrameworkElement + { + public static readonly DependencyProperty StrokesProperty = + DependencyProperty.Register(nameof(Strokes), typeof(StrokeCollection), typeof(InkPresenter), + new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender)); + + public StrokeCollection Strokes + { + get => (StrokeCollection)GetValue(StrokesProperty); + set => SetValue(StrokesProperty, value); + } + + protected override void OnRender(DrawingContext dc) + { + base.OnRender(dc); + if (Strokes == null) return; + foreach (var stroke in Strokes) + { + var geo = stroke.GetGeometry(); + if (geo != Geometry.Empty) + dc.DrawGeometry(null, new Pen(new SolidColorBrush(stroke.DrawingAttributes.Color), stroke.DrawingAttributes.Width), geo); + } + } + } +} diff --git a/UI/Controls/Ink/Stroke.cs b/UI/Controls/Ink/Stroke.cs new file mode 100644 index 00000000..5bbd0f4d --- /dev/null +++ b/UI/Controls/Ink/Stroke.cs @@ -0,0 +1,26 @@ +using System.Windows; +using System.Windows.Media; + +namespace InkkSlinger.UI.Controls.Ink +{ + public class Stroke + { + public StylusPointCollection StylusPoints { get; } + public DrawingAttributes DrawingAttributes { get; set; } + + public Stroke(StylusPointCollection stylusPoints, DrawingAttributes drawingAttributes) + { + StylusPoints = stylusPoints; + DrawingAttributes = drawingAttributes; + } + + public Geometry GetGeometry() + { + if (StylusPoints.Count < 2) return Geometry.Empty; + var fig = new PathFigure { StartPoint = new Point(StylusPoints[0].X, StylusPoints[0].Y) }; + for (int i = 1; i < StylusPoints.Count; i++) + fig.Segments.Add(new LineSegment(new Point(StylusPoints[i].X, StylusPoints[i].Y), true)); + return new PathGeometry { Figures = { fig } }; + } + } +} diff --git a/UI/Controls/Ink/StrokeCollection.cs b/UI/Controls/Ink/StrokeCollection.cs new file mode 100644 index 00000000..e420a668 --- /dev/null +++ b/UI/Controls/Ink/StrokeCollection.cs @@ -0,0 +1,6 @@ +using System.Collections.ObjectModel; + +namespace InkkSlinger.UI.Controls.Ink +{ + public class StrokeCollection : ObservableCollection { } +} diff --git a/UI/Controls/Ink/StylusPoint.cs b/UI/Controls/Ink/StylusPoint.cs new file mode 100644 index 00000000..f381f0ea --- /dev/null +++ b/UI/Controls/Ink/StylusPoint.cs @@ -0,0 +1,16 @@ +namespace InkkSlinger.UI.Controls.Ink +{ + public struct StylusPoint + { + public double X { get; } + public double Y { get; } + public float PressureFactor { get; } + + public StylusPoint(double x, double y, float pressureFactor = 0.5f) + { + X = x; + Y = y; + PressureFactor = pressureFactor; + } + } +} diff --git a/UI/Controls/Ink/StylusPointCollection.cs b/UI/Controls/Ink/StylusPointCollection.cs new file mode 100644 index 00000000..e2a11bf5 --- /dev/null +++ b/UI/Controls/Ink/StylusPointCollection.cs @@ -0,0 +1,6 @@ +using System.Collections.Generic; + +namespace InkkSlinger.UI.Controls.Ink +{ + public class StylusPointCollection : List { } +}