Support new features of Tizen.CircularUI (#188)
[platform/core/csapi/xsf.git] / src / XSF / Tizen.Wearable.CircularUI.Forms.Renderer / Shell / ShellSectionNavigationRenderer.cs
1 using ElmSharp;
2 using System;
3 using System.Collections.Generic;
4 using System.Linq;
5 using System.Threading.Tasks;
6 using Xamarin.Forms;
7 using Xamarin.Forms.Internals;
8 using Xamarin.Forms.Platform.Tizen;
9 using XForms = Xamarin.Forms.Forms;
10
11 namespace Tizen.Wearable.CircularUI.Forms.Renderer
12 {
13     class SimpleViewStack : Box
14     {
15         EvasObject _lastTop;
16
17         public SimpleViewStack(EvasObject parent) : base(parent)
18         {
19             InternalStack = new List<EvasObject>();
20             SetLayoutCallback(OnLayout);
21         }
22
23         List<EvasObject> InternalStack { get; set; }
24
25         public IReadOnlyList<EvasObject> Stack => InternalStack;
26
27         public void Push(EvasObject view)
28         {
29             InternalStack.Add(view);
30             PackEnd(view);
31             UpdateTopView();
32         }
33
34         public void Pop()
35         {
36             if (_lastTop != null)
37             {
38                 var tobeRemoved = _lastTop;
39                 InternalStack.Remove(tobeRemoved);
40                 UnPack(tobeRemoved);
41                 UpdateTopView();
42
43                 // if Pop was called by removed page,
44                 // Unrealize cause deletation of NativeCallback, it could be a cause of crash
45                 Device.BeginInvokeOnMainThread(() =>
46                 {
47                     tobeRemoved.Unrealize();
48                 });
49             }
50         }
51
52         public void PopToRoot()
53         {
54             while (InternalStack.Count > 1)
55             {
56                 Pop();
57             }
58         }
59
60         public void Insert(EvasObject before, EvasObject view)
61         {
62             view.Hide();
63             var idx = InternalStack.IndexOf(before);
64             InternalStack.Insert(idx, view);
65             PackEnd(view);
66             UpdateTopView();
67         }
68
69         public void Remove(EvasObject view)
70         {
71             InternalStack.Remove(view);
72             UnPack(view);
73             UpdateTopView();
74             Device.BeginInvokeOnMainThread(() =>
75             {
76                 view?.Unrealize();
77             });
78         }
79
80         void UpdateTopView()
81         {
82             if (_lastTop != InternalStack.LastOrDefault())
83             {
84                 _lastTop?.Hide();
85                 _lastTop = InternalStack.LastOrDefault();
86                 _lastTop.Show();
87             }
88         }
89
90         void OnLayout()
91         {
92             foreach (var view in Stack)
93             {
94                 view.Geometry = Geometry;
95             }
96         }
97         
98     }
99
100     public class ShellSectionNavigationRenderer : IShellItemRenderer
101     {
102         SimpleViewStack _viewStack;
103         IShellItemRenderer _rootPageRenderer;
104
105         public ShellSectionNavigationRenderer(ShellSection item)
106         {
107             ShellSection = item;
108             (ShellSection as IShellSectionController).NavigationRequested += OnNavigationRequested;
109             InitializeComponent();
110         }
111
112         public ShellSection ShellSection { get; protected set; }
113
114         public BaseShellItem Item => ShellSection;
115
116         public EvasObject NativeView => _viewStack;
117
118         public void Dispose()
119         {
120             Dispose(true);
121         }
122
123         protected virtual void Dispose(bool disposing)
124         {
125             if (disposing)
126             {
127                 _rootPageRenderer?.Dispose();
128                 _viewStack?.Unrealize();
129                 (ShellSection as IShellSectionController).NavigationRequested -= OnNavigationRequested;
130             }
131         }
132
133         void InitializeComponent()
134         {
135             _viewStack = new SimpleViewStack(XForms.NativeParent);
136             _viewStack.Show();
137
138             _rootPageRenderer = ShellRendererFactory.Default.CreateItemRenderer(ShellSection);
139             _viewStack.Push(_rootPageRenderer.NativeView);
140         }
141
142         void OnInsertRequest(NavigationRequestedEventArgs request)
143         {
144             var before = Platform.GetRenderer(request.BeforePage)?.NativeView ?? null;
145             if (before == null)
146             {
147                 request.Task = Task.FromException<bool>(new ArgumentException("Can't found page on stack", nameof(request.BeforePage)));
148                 return;
149             }
150             var renderer = Platform.GetOrCreateRenderer(request.Page);
151             _viewStack.Insert(before, renderer.NativeView);
152             request.Task = Task.FromResult(true);
153         }
154
155         void OnPushRequest(NavigationRequestedEventArgs request)
156         {
157             var renderer = Platform.GetOrCreateRenderer(request.Page);
158             _viewStack.Push(renderer.NativeView);
159             request.Task = Task.FromResult(true);
160         }
161
162         void OnPopRequest(NavigationRequestedEventArgs request)
163         {
164             _viewStack.Pop();
165             request.Task = Task.FromResult(true);
166         }
167
168         void OnPopToRootRequest(NavigationRequestedEventArgs request)
169         {
170             _viewStack.PopToRoot();
171             request.Task = Task.FromResult(true);
172         }
173
174         void OnRemoveRequest(NavigationRequestedEventArgs request)
175         {
176             var renderer = Platform.GetRenderer(request.Page);
177             if (renderer == null)
178             {
179                 request.Task = Task.FromException<bool>(new ArgumentException("Can't found page on stack", nameof(request.Page)));
180                 return;
181             }
182             _viewStack.Remove(renderer.NativeView);
183             request.Task = Task.FromResult(true);
184         }
185
186         void OnNavigationRequested(object sender, NavigationRequestedEventArgs e)
187         {
188             switch (e.RequestType)
189             {
190                 case NavigationRequestType.Insert:
191                     OnInsertRequest(e);
192                     break;
193                 case NavigationRequestType.Push:
194                     OnPushRequest(e);
195                     break;
196                 case NavigationRequestType.Pop:
197                     OnPopRequest(e);
198                     break;
199                 case NavigationRequestType.PopToRoot:
200                     OnPopToRootRequest(e);
201                     break;
202                 case NavigationRequestType.Remove:
203                     OnRemoveRequest(e);
204                     break;
205             }
206         }
207     }
208 }