171 lines
5.4 KiB
C#
171 lines
5.4 KiB
C#
using Microsoft.Maui.Graphics;
|
|
using Microsoft.Maui.Graphics.Skia;
|
|
using System.Collections.Generic;
|
|
|
|
namespace LSFE.MobApp.Views.CoveragePlan
|
|
{
|
|
public class SignaturePadView : GraphicsView
|
|
{
|
|
private readonly List<List<PointF>> _strokes = new();
|
|
private List<PointF> _currentStroke;
|
|
private readonly SignatureDrawable _drawable;
|
|
|
|
public SignaturePadView()
|
|
{
|
|
_drawable = new SignatureDrawable(_strokes, () => _currentStroke);
|
|
Drawable = _drawable;
|
|
BackgroundColor = Colors.White;
|
|
|
|
// Set minimum size to ensure it's touchable
|
|
MinimumHeightRequest = 200;
|
|
MinimumWidthRequest = 200;
|
|
|
|
StartInteraction += OnStartInteraction;
|
|
DragInteraction += OnDragInteraction;
|
|
EndInteraction += OnEndInteraction;
|
|
}
|
|
|
|
private void OnStartInteraction(object sender, TouchEventArgs e)
|
|
{
|
|
if (e.Touches.Length > 0)
|
|
{
|
|
_currentStroke = new List<PointF> { e.Touches[0] };
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
private void OnDragInteraction(object sender, TouchEventArgs e)
|
|
{
|
|
if (_currentStroke != null && e.Touches.Length > 0)
|
|
{
|
|
foreach (var touch in e.Touches)
|
|
{
|
|
_currentStroke.Add(touch);
|
|
}
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
private void OnEndInteraction(object sender, TouchEventArgs e)
|
|
{
|
|
if (_currentStroke != null && _currentStroke.Count > 0)
|
|
{
|
|
_strokes.Add(new List<PointF>(_currentStroke));
|
|
_currentStroke = null;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
_strokes.Clear();
|
|
_currentStroke = null;
|
|
Invalidate();
|
|
}
|
|
|
|
public bool IsEmpty()
|
|
{
|
|
return _strokes.Count == 0 && (_currentStroke == null || _currentStroke.Count == 0);
|
|
}
|
|
|
|
public async Task<byte[]> GetSignatureImageAsync()
|
|
{
|
|
// if nothing drawn, return null
|
|
if (IsEmpty())
|
|
return null;
|
|
|
|
// Determine size (fall back to default if not laid out yet)
|
|
var width = (int)Width;
|
|
var height = (int)Height;
|
|
if (width <= 0 || height <= 0)
|
|
{
|
|
width = 800;
|
|
height = 400;
|
|
}
|
|
|
|
// Choose display scale (1.0f is fine; you can use DeviceDisplay.MainDisplayInfo.Density if needed)
|
|
float displayScale = 1.0f;
|
|
|
|
try
|
|
{
|
|
// Create Skia bitmap export context
|
|
using var bmp = new SkiaBitmapExportContext(width, height, displayScale, dpi: 72, disposeBitmap: true, transparent: false);
|
|
var canvas = bmp.Canvas;
|
|
|
|
// Draw white background
|
|
canvas.FillColor = Colors.White;
|
|
canvas.FillRectangle(0, 0, width, height);
|
|
|
|
// Draw stored strokes
|
|
_drawable.Draw(canvas, new RectF(0, 0, width, height));
|
|
|
|
// Write PNG to memory stream
|
|
using var ms = new MemoryStream();
|
|
bmp.WriteToStream(ms);
|
|
|
|
return await Task.FromResult(ms.ToArray());
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine($"Error creating signature image: {ex.Message}");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private class SignatureDrawable : IDrawable
|
|
{
|
|
private readonly List<List<PointF>> _strokes;
|
|
private readonly Func<List<PointF>> _getCurrentStroke;
|
|
|
|
public SignatureDrawable(List<List<PointF>> strokes, Func<List<PointF>> getCurrentStroke)
|
|
{
|
|
_strokes = strokes;
|
|
_getCurrentStroke = getCurrentStroke;
|
|
}
|
|
|
|
public void Draw(ICanvas canvas, RectF dirtyRect)
|
|
{
|
|
canvas.StrokeColor = Colors.Black;
|
|
canvas.StrokeSize = 3;
|
|
canvas.StrokeLineCap = LineCap.Round;
|
|
canvas.StrokeLineJoin = LineJoin.Round;
|
|
|
|
// Draw completed strokes
|
|
foreach (var stroke in _strokes)
|
|
{
|
|
DrawStroke(canvas, stroke);
|
|
}
|
|
|
|
// Draw current stroke being drawn in real-time
|
|
var currentStroke = _getCurrentStroke();
|
|
if (currentStroke != null && currentStroke.Count > 0)
|
|
{
|
|
DrawStroke(canvas, currentStroke);
|
|
}
|
|
}
|
|
|
|
private void DrawStroke(ICanvas canvas, List<PointF> stroke)
|
|
{
|
|
if (stroke.Count > 1)
|
|
{
|
|
var path = new PathF();
|
|
path.MoveTo(stroke[0]);
|
|
|
|
for (int i = 1; i < stroke.Count; i++)
|
|
{
|
|
path.LineTo(stroke[i]);
|
|
}
|
|
|
|
canvas.DrawPath(path);
|
|
}
|
|
else if (stroke.Count == 1)
|
|
{
|
|
// Draw single point as small dot
|
|
var p = stroke[0];
|
|
canvas.FillRectangle(p.X - 1.5f, p.Y - 1.5f, 3, 3);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|