Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
AnimatedCheckbox.cs
1using System.Globalization;
2
4{
5// AnimatedCheckbox.cs
6public class AnimatedCheckbox : ContentView
7{
8 readonly GraphicsView _gfx;
9 readonly CheckboxDrawable _drawable;
10 bool _isChecked;
11 double _scale = 1.0;
12
13 public static readonly BindableProperty IsCheckedProperty =
14 BindableProperty.Create(nameof(IsChecked), typeof(bool), typeof(AnimatedCheckbox), false,
15 propertyChanged: (b, o, n) => ((AnimatedCheckbox)b).AnimateTo((bool)n));
16
17 public bool IsChecked
18 {
19 get => (bool)GetValue(IsCheckedProperty);
20 set => SetValue(IsCheckedProperty, value);
21 }
22
23 public static readonly BindableProperty StrokeColorProperty =
24 BindableProperty.Create(
25 nameof(StrokeColor),
26 typeof(Color),
27 typeof(AnimatedCheckbox),
28 Colors.Black,
29 propertyChanged: (b, o, n) => ((AnimatedCheckbox)b).OnStrokeColorChanged((Color)n));
30
31 public static readonly BindableProperty FillColorProperty =
32 BindableProperty.Create(
33 nameof(FillColor),
34 typeof(Color),
35 typeof(AnimatedCheckbox),
36 Colors.Transparent,
37 propertyChanged: (b, o, n) => ((AnimatedCheckbox)b).OnFillColorChanged((Color)n));
38
39 public static readonly BindableProperty StrokeWidthProperty =
40 BindableProperty.Create(
41 nameof(StrokeWidth),
42 typeof(float),
43 typeof(AnimatedCheckbox),
44 2f,
45 propertyChanged: (b, o, n) => ((AnimatedCheckbox)b).OnStrokeWidthChanged((float)n));
46
47 public Color StrokeColor
48 {
49 get => (Color)GetValue(StrokeColorProperty);
50 set => SetValue(StrokeColorProperty, value);
51 }
52
53 public Color FillColor
54 {
55 get => (Color)GetValue(FillColorProperty);
56 set => SetValue(FillColorProperty, value);
57 }
58
59 public float StrokeWidth
60 {
61 get => (float)GetValue(StrokeWidthProperty);
62 set => SetValue(StrokeWidthProperty, value);
63 }
64
65 public AnimatedCheckbox()
66 {
67 _drawable = new CheckboxDrawable();
68 _gfx = new GraphicsView { Drawable = _drawable, HeightRequest = 28, WidthRequest = 28 };
69 Content = new Grid { Children = { _gfx } };
70
71 var tap = new TapGestureRecognizer();
72 tap.Tapped += (_, __) => IsChecked = !IsChecked;
73 // Attach to both the container and the graphics view to ensure taps are captured
74 GestureRecognizers.Add(tap);
75 _gfx.GestureRecognizers.Add(new TapGestureRecognizer { Command = new Command(() => IsChecked = !IsChecked) });
76
77 // Accessibility
78 this.SetBinding(SemanticProperties.DescriptionProperty, new Binding(nameof(this.IsChecked), source: this, converter: new BoolToStr("Checked", "Unchecked")));
79 }
80
81 void OnStrokeColorChanged(Color color)
82 {
83 if (_drawable is not null)
84 {
85 _drawable.StrokeColor = color;
86 _gfx?.Invalidate();
87 }
88 }
89
90 void OnFillColorChanged(Color color)
91 {
92 if (_drawable is not null)
93 {
94 _drawable.FillColor = color;
95 _gfx?.Invalidate();
96 }
97 }
98
99 void OnStrokeWidthChanged(float width)
100 {
101 if (_drawable is not null)
102 {
103 _drawable.StrokeWidth = width;
104 _gfx?.Invalidate();
105 }
106 }
107
108 void AnimateTo(bool targetChecked)
109 {
110 var from = _drawable.Progress;
111 var to = targetChecked ? 1.0 : 0.0;
112
113 var anim = new Animation(p =>
114 {
115 _drawable.Progress = p;
116 _gfx.Invalidate();
117 }, from, to, Easing.CubicOut);
118
119 // little pop
120 var pop = new Animation(s => { _scale = s; this.Scale = _scale; }, 1, 1.06, Easing.CubicOut);
121 var popBack = new Animation(s => { _scale = s; this.Scale = _scale; }, 1.06, 1, Easing.CubicIn);
122
123 var parent = new Animation();
124 parent.Add(0, 1, anim);
125 parent.Add(0, 0.5, pop);
126 parent.Add(0.5, 1, popBack);
127
128 parent.Commit(this, "chk", length: 180);
129 }
130
131 class BoolToStr : IValueConverter
132 {
133 string t, f; public BoolToStr(string t, string f) { this.t=t; this.f=f; }
134 public object Convert(object? value, Type ttype, object? p, CultureInfo c) => (bool)value ? t : f;
135 public object ConvertBack(object? v, Type ttype, object? p, CultureInfo c) => throw new NotImplementedException();
136 }
137}
138
139// CheckboxDrawable.cs
140public class CheckboxDrawable : IDrawable
141{
142 public double Progress { get; set; } = 0; // 0..1
143 public Color StrokeColor { get; set; } = Colors.Black;
144 public Color FillColor { get; set; } = Colors.Transparent;
145 public float StrokeWidth { get; set; } = 2f;
146 const float Corner = 6f;
147
148 public void Draw(ICanvas canvas, RectF rect)
149 {
150 canvas.SaveState();
151
152 // Box
153 var box = rect.Inflate(-2, -2);
154 canvas.FillColor = FillColor;
155 canvas.StrokeColor = StrokeColor;
156 canvas.StrokeSize = StrokeWidth;
157 canvas.FillRoundedRectangle(box, Corner);
158 canvas.DrawRoundedRectangle(box, Corner);
159
160 // Checkmark path points (relative)
161 // Points tuned for a 28x28 box; scale to rect
162 var p1 = new PointF(box.X + box.Width * 0.26f, box.Y + box.Height * 0.54f);
163 var p2 = new PointF(box.X + box.Width * 0.44f, box.Y + box.Height * 0.72f);
164 var p3 = new PointF(box.X + box.Width * 0.76f, box.Y + box.Height * 0.34f);
165
166 // Draw “from nothing” by interpolating along the polyline length
167 var total = Distance(p1, p2) + Distance(p2, p3);
168 var drawLen = (float)(total * Progress);
169
170 canvas.StrokeLineCap = LineCap.Round;
171 canvas.StrokeLineJoin = LineJoin.Round;
172
173 // Segment 1
174 var d1 = Distance(p1, p2);
175 if (drawLen > 0)
176 {
177 if (drawLen <= d1)
178 {
179 var t = drawLen / d1;
180 var mid = Lerp(p1, p2, t);
181 canvas.DrawLine(p1, mid);
182 }
183 else
184 {
185 canvas.DrawLine(p1, p2);
186 // Segment 2
187 var rem = drawLen - d1;
188 var t = Math.Clamp(rem / Distance(p2, p3), 0, 1);
189 var mid = Lerp(p2, p3, t);
190 canvas.DrawLine(p2, mid);
191 }
192 }
193
194 canvas.RestoreState();
195 }
196
197 static float Distance(PointF a, PointF b) => (float)Math.Sqrt((a.X-b.X)*(a.X-b.X)+(a.Y-b.Y)*(a.Y-b.Y));
198 static PointF Lerp(PointF a, PointF b, float t) => new(a.X + (b.X-a.X)*t, a.Y + (b.Y-a.Y)*t);
199}
200
201}
Definition: ImplTypes.g.cs:58