| uti | id | title | platforms | packages | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
com.xamarin.workbook |
3f9c2e8c-577a-47e1-bd7f-8ec872ff5c29 |
SkiaSharp on iOS |
|
|
#r "SkiaSharp"#r "SkiaSharp.Views.iOS"// SkiaSharp usings
using SkiaSharp;
using SkiaSharp.Views;
using SkiaSharp.Views.iOS;
//
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
// iOS usings
using UIKit;var controller = RootViewController;public abstract class Sprite
{
public Action SetNeedsDisplay { get; set; }
public Sprite()
{
SetNeedsDisplay = () => { Debug.WriteLine(this.ToString() + " Needs Display"); };
}
Lazy<uint> randColor => new Lazy<uint>(() => (uint)(new Random(this.GetType().Name.GetHashCode()).NextDouble() * 0xFFFFFFFF));
protected virtual SKPaint BackgroundPaint { get; } = new SKPaint {
Color = 0x00000000 //SkColors.Transparent
};
public void Draw(SKCanvas canvas, SKRect size, bool isDebug=false)
{
var rect = GetRect(size);
DrawRect(canvas, rect);
if (isDebug)
{
canvas.DrawRect(rect, new SKPaint
{
Color = new SKColor(randColor.Value),
Style = SKPaintStyle.Stroke,
});
}
}
protected virtual void DrawRect(SKCanvas canvas, SKRect size)
{
canvas.DrawRect(size, BackgroundPaint);
}
public abstract SKRect GetRect(SKRect canvasRect);
}
public abstract class Button : Sprite
{
protected Action<Button> OnPressAction { get; set; }
public void OnPress()
{
OnPressAction?.Invoke(this);
Console.WriteLine(this);
SetNeedsDisplay?.Invoke();
}
}
public abstract class Label : Button
{
protected abstract string Text { get; }
protected abstract SKPaint Paint { get; }
protected override void DrawRect(SKCanvas canvas, SKRect size)
{
base.DrawRect(canvas, size);
canvas.DrawText(
Text,
x: size.Left + (size.Width/2),
y: size.Top + (size.Height/2) + (Paint.TextSize/2),
paint:Paint
);
}
}
public class LabelImpl : Label
{
public LabelImpl(Func<string> text, SKPaint paint)
{
_text = text;
_paint = paint;
}
private Func<string> _text;
protected override string Text => _text?.Invoke();
private SKPaint _paint;
protected override SKPaint Paint => _paint;
public override SKRect GetRect(SKRect canvasRect)
{
return new SKRect(canvasRect.Left, canvasRect.Top, canvasRect.Width, Paint.TextSize *2);
}
}// create the view
var skiaCanvasView = new SKCanvasView(controller.View.Bounds);
// add it to the window
controller.View.AddSubview(skiaCanvasView);void SetAndNotify<T>(/*Func<T> prop,*/ T value, SKCanvasView view)
{
view.SetNeedsDisplay();
}
void SetNeedsDisplay(Sprite sprite, SKCanvasView view)
{
var rect = CGRect.Empty;//TODO : from view.GetRect()
view.SetNeedsDisplayInRect(rect);
}//static class DateTimeExtensions {
static GregorianCalendar _gc = new GregorianCalendar();
public static int GetDayPositionInWeek(this DateTime time)
=> (((int)time.DayOfWeek)+6)%7; //TODO : depends on the culture ?
public static int GetWeekOfMonth(this DateTime time, DateTime first)
{
return time.GetWeekOfYear() - first.GetWeekOfYear();
}
static int GetWeekOfYear(this DateTime time)
=> _gc.GetWeekOfYear(time, CalendarWeekRule.FirstDay, DayOfWeek.Monday);
//}public class Paints
{
private static SKTypeface font = SKTypeface.FromFamilyName("Trebuchet");
public SKPaint paint = new SKPaint {
IsAntialias = true,
TextSize = 42,
TextAlign = SKTextAlign.Center,
Color = 0xFF000000,
Style = SKPaintStyle.Fill,
Typeface = font,
};
public SKPaint paintToday = new SKPaint {
IsAntialias = true,
TextSize = 42,
TextAlign = SKTextAlign.Center,
Color = 0xFF0000FF,
Style = SKPaintStyle.Fill,
Typeface = font,
};
public SKPaint paintWE = new SKPaint {
IsAntialias = true,
TextSize = 42,
TextAlign = SKTextAlign.Center,
Color = 0xFFB0B0B0,
Style = SKPaintStyle.Fill,
Typeface = font,
};
public SKPaint paintSelected = new SKPaint {
IsAntialias = true,
Color = 0xFFB0B0B0,
Style = SKPaintStyle.Fill,
};
}
var paints = new Paints();class Day : Label
{
Paints _paints;
int _selectedMonth;
public DateTime Date { get; private set; }
public Day(DateTime d, int selectedMonth, Paints paints, Action<DateTime> onPress, bool isSelected = false)
{
Date = d;
_paints = paints;
_selectedMonth = selectedMonth;
OnPressAction = bt => onPress?.Invoke(Date);
IsSelected = isSelected;
}
public int DayPos => Date.GetDayPositionInWeek();
public int WeekPos => Date.GetWeekOfMonth(new DateTime(Date.Year, _selectedMonth, 1));
public bool IsInWeekEnd => Date.DayOfWeek == DayOfWeek.Saturday || Date.DayOfWeek == DayOfWeek.Sunday;
public bool IsOutsideOfSelectedMonth => Date.Month != _selectedMonth;
public bool IsToday => Date.Date == DateTime.Now.Date;
public bool IsPast => Date.Date < DateTime.Now.Date;
public bool IsSelected { get; set; } = false;
protected override string Text => Date.Day.ToString();
protected override SKPaint Paint =>
IsToday
? _paints.paintToday
: IsInWeekEnd || IsOutsideOfSelectedMonth || IsPast
? _paints.paintWE
: _paints.paint;
protected override SKPaint BackgroundPaint =>
IsSelected
? _paints.paintSelected
: base.BackgroundPaint;
public override SKRect GetRect (SKRect size)
{
size.Top = 80;
var w = size.Width / 7;
var h = size.Height / 5; //TODO get totalWeeks instead of 5
return SKRect.Create(
x: size.Left + (DayPos * w),
y: size.Top + (WeekPos * h),
width: w,
height: h
);
}
}class PrevNext : Label
{
int _direction;
private string _label;
protected override string Text => _label;
private SKPaint _paint;
protected override SKPaint Paint => _paint;
public PrevNext(string label, int direction, SKPaint paint, Action<int> action)
{
_label = label;
_direction = direction;
_paint = paint;
this.OnPressAction = bt => action?.Invoke(direction);
}
public override SKRect GetRect (SKRect size)
{
var w = 64;
var h = 64;
return SKRect.Create(
x: _direction < 0 ? 0 : size.Width - w,
y: size.Top,
width: w,
height: h
);
}
}var displayList = new List<Sprite>();DateTime selectedDate, selectedMonth;
selectedDate = selectedMonth = DateTime.Now.Date;
//DateTime SelectedDate { get => selectedDate; set => SetAndNotify(/*v=>selectedDate=v,*/ value, skiaCanvasView); }
void ChangeMonth(int direction)
{
selectedMonth = new DateTime(selectedMonth.Year, selectedMonth.Month, 1).AddMonths(direction); // TODO: selectedmonth + redraw
var firstDay = new DateTime(selectedMonth.Year, selectedMonth.Month, 1);
var firstDayOfFirstWeek = firstDay.AddDays(-firstDay.GetDayPositionInWeek());
var lastDay = new DateTime(selectedMonth.Year, selectedMonth.Month, DateTime.DaysInMonth(selectedMonth.Year, selectedMonth.Month));
var lastDayOfLastWeek = lastDay.AddDays(7 - 1 - lastDay.GetDayPositionInWeek());
var totalDays = (lastDayOfLastWeek - firstDayOfFirstWeek).Days + 1;
var totalWeeks = (totalDays) / 7;
displayList.RemoveAll(s => s is Day);
for (int d=0; d < totalDays; d++){
var day = firstDayOfFirstWeek.AddDays(d);
displayList.Add(new Day(day, selectedMonth.Month, paints, SelectDate, selectedDate.Date == day.Date));
}
skiaCanvasView.SetNeedsDisplay();
}
void SelectDate(DateTime time)
{
selectedDate = time;
var days = displayList.Where(s => s is Day);
foreach (Sprite s in days)
(s as Day).IsSelected = (s as Day).Date.Date == time.Date;
//TODO shouldn't be needed, SetNeedsDisplay from Sprite should call it
skiaCanvasView.SetNeedsDisplay();
}ChangeMonth(0);displayList.Add(new LabelImpl(() => selectedMonth.ToString("MMMM yyyy"), paints.paint));
displayList.Add(new PrevNext("<", -1, paints.paint, ChangeMonth));
displayList.Add(new PrevNext(">", +1, paints.paint, ChangeMonth));displayListSKRect rect;T GetSpriteAt<T>(SKPoint point, SKRect rect)
where T:Sprite
=> displayList
.Where(s => s is T)
.LastOrDefault(s => s.GetRect(rect).Contains(point))
as T
;void Draw(SKCanvas canvas, SKSize size)
{
canvas.Clear(SKColors.White);
rect = new SKRect(0, 0, size.Width, size.Height/2);
// display list
foreach(Sprite sprite in displayList)
{
sprite.SetNeedsDisplay = () => SetNeedsDisplay(sprite, skiaCanvasView);
sprite.Draw(canvas, rect, isDebug:false);
}
}//var b = GetSpriteAt<Button>(new SKPoint(200, 200), rect);skiaCanvasView.PaintSurface += OnPaintSurface;
void OnPaintSurface(object sender, SKPaintSurfaceEventArgs e)
{
var canvas = e.Surface.Canvas;
var size = e.Info.Size;
Draw(canvas, size);
}
// trigger a refresh
skiaCanvasView.SetNeedsDisplay();public class SKTouchEventArgs : EventArgs
{
public SKTouchEventArgs(long id, SKTouchAction type, SKPoint location, bool inContact)
: this(id, type, SKMouseButton.Left, SKTouchDeviceType.Touch, location, inContact)
{
}
public SKTouchEventArgs(long id, SKTouchAction type, SKMouseButton mouseButton, SKTouchDeviceType deviceType, SKPoint location, bool inContact)
{
Id = id;
ActionType = type;
DeviceType = deviceType;
MouseButton = mouseButton;
Location = location;
InContact = inContact;
}
public bool Handled { get; set; }
public long Id { get; private set; }
public SKTouchAction ActionType { get; private set; }
public SKTouchDeviceType DeviceType { get; private set; }
public SKMouseButton MouseButton { get; private set; }
public SKPoint Location { get; private set; }
public bool InContact { get; private set; }
public override string ToString()
{
return $"{{ActionType={ActionType}, DeviceType={DeviceType}, Handled={Handled}, Id={Id}, InContact={InContact}, Location={Location}, MouseButton={MouseButton}}}";
}
}
public enum SKTouchAction
{
Entered,
Pressed,
Moved,
Released,
Cancelled,
Exited,
}
public enum SKTouchDeviceType
{
Touch,
Mouse,
Pen
}
public enum SKMouseButton
{
Unknown,
Left,
Middle,
Right
}skiaCanvasView.AddGestureRecognizer(new PressGestureReco(skiaCanvasView, OnSliderTouchOrMove));
float x,y;
private void OnSliderTouchOrMove(SKTouchEventArgs sk)
{
x = sk.Location.X * (float)skiaCanvasView.ContentScaleFactor;
y = sk.Location.Y * (float)skiaCanvasView.ContentScaleFactor;
var b = GetSpriteAt<Button>(new SKPoint(x, y), rect);
b?.OnPress();
}
private class PressGestureReco : UITapGestureRecognizer
{
private readonly SKCanvasView _view;
private readonly Action<SKTouchEventArgs> _onTouchAction;
public PressGestureReco(SKCanvasView view, Action<SKTouchEventArgs> onTouchAction)
{
_view = view;
_onTouchAction = onTouchAction;
}
public override void TouchesBegan(NSSet touches, UIEvent evt)
{
base.TouchesBegan(touches, evt);
UITouch t = touches.AnyObject as UITouch;
var point = t.LocationInView(_view);
_onTouchAction(new SKTouchEventArgs(0, SKTouchAction.Moved,
new SKPoint((float)point.X, (float)point.Y), true));
}
}GetVars()displayList[19].GetRect(rect);GetSpriteAt<Button>(new SKPoint(x, y), rect)