2 using ElmSharp.Wearable;
4 using System.Threading;
5 using System.Threading.Tasks;
7 using Xamarin.Forms.Platform.Tizen;
8 using EColor = ElmSharp.Color;
9 using EImage = ElmSharp.Image;
10 using ELayout = ElmSharp.Layout;
11 using EWidget = ElmSharp.Widget;
12 using EButton = ElmSharp.Button;
14 namespace Tizen.Wearable.CircularUI.Forms.Renderer
16 public class NavigationDrawer : ELayout, IAnimatable
18 static readonly int TouchWidth = 50;
19 static readonly int IconSize = 40;
20 static readonly string DefaultIcon = "Tizen.Wearable.CircularUI.Forms.Renderer.res.wc_visual_cue.png";
23 Box _contentGestureBox;
26 Box _drawerContentBox;
30 EvasObject _drawerContent;
35 GestureLayer _gestureOnContent;
36 GestureLayer _gestureOnDrawer;
38 ImageSource _drawerIconSource;
43 CancellationTokenSource _fadeInCancelTokenSource = null;
45 bool HasDrawer => _drawerBox != null;
47 public NavigationDrawer(EvasObject parent) : base(parent)
52 public int HandlerHeight { get; set; } = 40;
76 EColor _handlerBackgroundColor = EColor.Transparent;
77 public EColor HandlerBackgroundColor
79 get => _handlerBackgroundColor;
82 _handlerBackgroundColor = value;
83 UpdateHandlerBackgroundColor();
87 public event EventHandler Toggled;
89 public void SetMainContent(EvasObject content)
99 _contentBox.PackEnd(_content);
100 _content.Geometry = _contentBox.Geometry;
103 public void SetDrawerContent(EvasObject content)
105 InitializeDrawerBox();
109 UnsetDrawerContent();
113 _drawerContent = content;
114 _drawerContent.Show();
115 _drawerContentBox.PackEnd(_drawerContent);
117 _drawerContentBox.Show();
118 _drawerIconBox.Show();
120 if (_drawerContent is NavigationView nv)
122 nv.Dragged += (s, e) =>
124 if (e.State == DraggedState.EdgeTop)
132 public void UpdateDrawerIcon(ImageSource source)
134 _drawerIconSource = source;
137 SetDrawerIcon(_drawerIconSource);
141 public async void Open(uint length = 300)
146 var toMove = _drawerBox.Geometry;
149 await RunMoveAnimation(_drawerBox, toMove, length);
154 Toggled?.Invoke(this, EventArgs.Empty);
161 public async void Close(uint length = 300)
166 var toMove = _drawerBox.Geometry;
167 toMove.Y = Geometry.Height - HandlerHeight;
169 await RunMoveAnimation(_drawerBox, toMove, length);
174 Toggled?.Invoke(this, EventArgs.Empty);
179 StartHighlightAnimation(_drawerIcon);
182 void IAnimatable.BatchBegin()
186 void IAnimatable.BatchCommit()
190 protected override IntPtr CreateHandle(EvasObject parent)
192 _mainLayout = new Box(parent);
193 return _mainLayout.Handle;
198 _mainLayout.SetLayoutCallback(OnLayout);
200 _contentGestureBox = new Box(_mainLayout);
201 _contentGestureBox.Show();
202 _mainLayout.PackEnd(_contentGestureBox);
204 _contentBox = new Box(_mainLayout);
205 _contentBox.SetLayoutCallback(OnContentLayout);
207 _mainLayout.PackEnd(_contentBox);
210 void InitializeDrawerBox()
212 if (_drawerBox != null)
215 _drawerBox = new Box(_mainLayout);
216 _drawerBox.SetLayoutCallback(OnDrawerLayout);
218 _mainLayout.PackEnd(_drawerBox);
220 _drawerContentBox = new Box(_drawerBox);
221 _drawerBox.PackEnd(_drawerContentBox);
223 _drawerIconBox = new Box(_drawerBox)
225 BackgroundColor = _handlerBackgroundColor
227 _drawerBox.PackEnd(_drawerIconBox);
229 _drawerIcon = new EImage(_drawerIconBox)
233 MinimumHeight = IconSize,
234 MinimumWidth = IconSize,
237 _drawerIconBox.PackEnd(_drawerIcon);
238 SetDrawerIcon(_drawerIconSource);
240 _touchArea = new EButton(_drawerBox)
242 Color = EColor.Transparent,
243 BackgroundColor = EColor.Transparent,
245 _touchArea.SetPartColor("effect", EColor.Transparent);
247 _touchArea.RepeatEvents = true;
248 _touchArea.Clicked += OnIconClicked;
250 _drawerBox.PackEnd(_touchArea);
252 _gestureOnContent = new GestureLayer(_contentGestureBox);
253 _gestureOnContent.SetMomentumCallback(GestureLayer.GestureState.Start, OnContentDragStarted);
254 _gestureOnContent.SetMomentumCallback(GestureLayer.GestureState.End, OnContentDragEnded);
255 _gestureOnContent.SetMomentumCallback(GestureLayer.GestureState.Abort, OnContentDragEnded);
256 _gestureOnContent.Attach(_contentGestureBox);
257 _contentBox.RepeatEvents = true;
259 _gestureOnDrawer = new GestureLayer(_drawerIconBox);
260 _gestureOnDrawer.SetMomentumCallback(GestureLayer.GestureState.Move, OnDrawerDragged);
261 _gestureOnDrawer.SetMomentumCallback(GestureLayer.GestureState.End, OnDrawerDragEnded);
262 _gestureOnDrawer.SetMomentumCallback(GestureLayer.GestureState.Abort, OnDrawerDragEnded);
263 _gestureOnDrawer.Attach(_drawerIconBox);
265 RotaryEventManager.Rotated += OnRotateEventReceived;
268 void SetDrawerIcon(ImageSource source)
272 _drawerIcon.LoadFromImageSourceAsync(ImageSource.FromResource(DefaultIcon, GetType().Assembly));
273 _isDefaultIcon = true;
277 _isDefaultIcon = false;
278 if (source is FileImageSource fsource)
280 _drawerIcon.Load(fsource.ToAbsPath());
284 _drawerIcon.LoadFromImageSourceAsync(source);
289 void UpdateHandlerBackgroundColor()
291 if (_drawerIconBox != null)
293 _drawerIconBox.BackgroundColor = _handlerBackgroundColor;
297 void OnIconClicked(object sender, EventArgs e)
305 async Task<bool> ShowAsync(EWidget target, Easing easing = null, uint length = 300, CancellationToken cancelltaionToken = default(CancellationToken))
307 var tcs = new TaskCompletionSource<bool>();
309 await Task.Delay(1000);
311 if (cancelltaionToken.IsCancellationRequested)
313 cancelltaionToken.ThrowIfCancellationRequested();
317 var opacity = target.Opacity;
319 if (opacity == 255 || opacity == -1)
322 new Animation((progress) =>
324 target.Opacity = opacity + (int)((255 - opacity) * progress);
326 }).Commit(this, "FadeIn", length: length, finished: (p, e) =>
328 target.Opacity = 255;
330 StartHighlightAnimation(_drawerIcon);
333 return await tcs.Task;
338 var bound = Geometry;
339 _contentGestureBox.Geometry = bound;
340 _contentBox.Geometry = bound;
341 if (_drawerBox != null)
343 bound.Y = _isOpen ? 0 : (bound.Height - HandlerHeight);
344 _drawerBox.Geometry = bound;
348 void OnContentLayout()
350 if (_content != null)
352 _content.Geometry = _contentBox.Geometry;
356 void OnDrawerLayout()
358 this.AbortAnimation("HighlightAnimation");
360 var bound = _drawerBox.Geometry;
362 var currentY = bound.Y;
363 var ratio = currentY / (double)(Geometry.Height - HandlerHeight);
365 var contentBound = bound;
366 contentBound.Y += (int)(HandlerHeight * ratio);
367 _drawerContentBox.Geometry = contentBound;
369 var drawerHandleBound = bound;
370 drawerHandleBound.Height = HandlerHeight;
371 _drawerIconBox.Geometry = drawerHandleBound;
373 var drawerTouchBound = drawerHandleBound;
374 drawerTouchBound.Width = TouchWidth;
375 drawerTouchBound.X = drawerHandleBound.X + (drawerHandleBound.Width - TouchWidth) / 2;
376 _touchArea.Geometry = drawerTouchBound;
379 async Task<bool> HideAsync(EWidget target, Easing easing = null, uint length = 300)
381 var tcs = new TaskCompletionSource<bool>();
383 var opacity = target.Opacity;
387 new Animation((progress) =>
389 target.Opacity = opacity - (int)(progress * opacity);
391 }).Commit(this, "FadeOut", length: length, finished: (p, e) =>
398 return await tcs.Task;
401 void StartHighlightAnimation(EWidget target)
403 if (!_isDefaultIcon || this.AnimationIsRunning("HighlightAnimation"))
407 var bound = target.Geometry;
409 var dy = bound.Y - bound.Height / 3;
411 var anim = new Animation();
413 var transfAnim = new Animation((f) =>
416 var map = new EvasMap(4);
417 map.PopulatePoints(bound, 0);
418 target.IsMapEnabled = true;
419 target.EvasMap = map;
422 var opacityAnim = new Animation(f => target.Opacity = (int)f, 255, 40);
424 anim.Add(0, 1, opacityAnim);
425 anim.Add(0, 1, transfAnim);
427 anim.Commit(this, "HighlightAnimation", 16, 800, finished: (f, b) =>
429 target.Opacity = 255;
430 target.IsMapEnabled = false;
431 }, repeat:() => --count > 0);
434 async void OnRotateEventReceived(EventArgs args)
436 _fadeInCancelTokenSource?.Cancel();
437 _fadeInCancelTokenSource = new CancellationTokenSource();
441 var token = _fadeInCancelTokenSource.Token;
442 await HideAsync(_drawerBox);
443 _ = ShowAsync(_drawerBox, cancelltaionToken: token);
447 void OnContentDragStarted(GestureLayer.MomentumData moment)
449 _fadeInCancelTokenSource?.Cancel();
450 _fadeInCancelTokenSource = null;
454 _ = HideAsync(_drawerBox);
458 void OnContentDragEnded(GestureLayer.MomentumData moment)
460 _fadeInCancelTokenSource = new CancellationTokenSource();
461 _ = ShowAsync(_drawerBox, cancelltaionToken: _fadeInCancelTokenSource.Token);
464 void OnDrawerDragged(GestureLayer.MomentumData moment)
466 var toMove = _drawerBox.Geometry;
467 toMove.Y = (moment.Y2 < 0) ? 0 : moment.Y2;
468 _drawerBox.Geometry = toMove;
472 void OnDrawerDragEnded(GestureLayer.MomentumData moment)
474 if (_drawerBox.Geometry.Y < (_mainLayout.Geometry.Height / 2))
488 _drawerIcon.Orientation = ImageOrientation.FlipVertical;
494 _drawerIcon.Orientation = ImageOrientation.None;
497 Task RunMoveAnimation(EvasObject target, Rect dest, uint length, Easing easing = null)
499 var tcs = new TaskCompletionSource<bool>();
501 var dx = target.Geometry.X - dest.X;
502 var dy = target.Geometry.Y - dest.Y;
504 new Animation((progress) =>
507 toMove.X += (int)(dx * (1 - progress));
508 toMove.Y += (int)(dy * (1 - progress));
509 target.Geometry = toMove;
511 }).Commit(this, "Move", length: length, finished: (s, e) =>
513 target.Geometry = dest;
519 void UnsetMainContent()
521 if (_content != null)
523 _contentBox.UnPack(_content);
529 void UnsetDrawerContent()
531 if (_drawerContent != null)
533 _drawerContentBox.UnPack(_drawerContent);
534 _drawerContent.Hide();
535 _drawerContent = null;
537 _drawerContentBox.Hide();
538 _drawerIconBox.Hide();