Support new features of Tizen.CircularUI (#188)
[platform/core/csapi/xsf.git] / src / XSF / Tizen.Wearable.CircularUI.Forms.Renderer / Shell / NavigationView.cs
1 using ElmSharp;
2 using ElmSharp.Wearable;
3 using System;
4 using System.Collections.Generic;
5 using Xamarin.Forms;
6 using Xamarin.Forms.Platform.Tizen;
7 using ELayout = ElmSharp.Layout;
8 using EColor = ElmSharp.Color;
9
10 namespace Tizen.Wearable.CircularUI.Forms.Renderer
11 {
12     public class NavigationView : ELayout
13     {
14         readonly int _dafaultIconSize = 60;
15
16         class Item
17         {
18             public Element Source { get; set; } 
19             public string Text { get; set; }
20             public string Icon { get; set; }
21         }
22
23         Box _outterBox;
24         ELayout _surfaceLayout;
25         CircleSurface _surface;
26         CircleGenList _naviMenu;
27
28         GenItemClass _defaultClass;
29         SmartEvent _draggedUpCallback;
30         SmartEvent _draggedDownCallback;
31
32         GenListItem _header;
33         GenListItem _footer;
34
35         List<List<Element>> _itemCache;
36         List<GenListItem> _items = new List<GenListItem>();
37
38         public NavigationView(EvasObject parent) : base(parent)
39         {
40             InitializeComponent();
41         }
42
43         public event EventHandler<SelectedItemChangedEventArgs> ItemSelected;
44
45         public event EventHandler<DraggedEventArgs> Dragged;
46
47
48         EColor _backgroundColor = EColor.Black;
49         public override EColor BackgroundColor
50         {
51             get => _backgroundColor;
52             set
53             {
54                 _backgroundColor = value.IsDefault ? EColor.Black : value;
55                 UpdateBackgroundColor();
56             }
57         }
58
59         EColor _foregroundColor = EColor.Default;
60         public EColor ForegroundColor
61         {
62             get => _foregroundColor;
63             set
64             {
65                 _foregroundColor = value;
66                 UpdateForegroundColor();
67             }
68         }
69
70
71         public void Build(List<List<Element>> items)
72         {
73             // Only update when items was changed
74             if (!IsUpdated(items))
75             {
76                 return;
77             }
78             _itemCache = items;
79
80             _naviMenu.Clear();
81             _items.Clear();
82             // header
83             _header = _naviMenu.Append(_defaultClass, new Item { Text = "" });
84
85             // TODO. need to improve, need to support group
86             foreach (var group in items)
87             {
88                 foreach (var item in group)
89                 {
90                     var data = new Item
91                     {
92                         Source = item
93                     };
94                     if (item is BaseShellItem shellItem)
95                     {
96                         data.Text = shellItem.Title;
97                         data.Icon = (shellItem.Icon as FileImageSource)?.ToAbsPath();
98                     }
99                     else if (item is MenuItem menuItem)
100                     {
101                         data.Text = menuItem.Text;
102                         data.Icon = (menuItem.IconImageSource as FileImageSource)?.ToAbsPath();
103                     }
104                     var genitem = _naviMenu.Append(_defaultClass, data, GenListItemType.Normal);
105                     genitem.SetPartColor("bg", _backgroundColor);
106                     _items.Add(genitem);
107                 }
108             }
109             _footer = _naviMenu.Append(_defaultClass, new Item { Text = "" });
110         }
111
112         public void Activate()
113         {
114             (_naviMenu as IRotaryActionWidget)?.Activate();
115         }
116         public void Deactivate()
117         {
118             (_naviMenu as IRotaryActionWidget)?.Deactivate();
119         }
120
121         protected override IntPtr CreateHandle(EvasObject parent)
122         {
123             _outterBox = new Box(parent);
124             return _outterBox.Handle;
125         }
126
127         void InitializeComponent()
128         {
129             _outterBox.SetLayoutCallback(OnLayout);
130
131             _surfaceLayout = new ELayout(this);
132             _surfaceLayout.Show();
133             _surface = new CircleSurface(_surfaceLayout);
134
135             _naviMenu = new CircleGenList(this, _surface)
136             {
137                 Homogeneous = true,
138                 BackgroundColor = _backgroundColor
139             };
140             _naviMenu.Show();
141
142             _draggedUpCallback = new SmartEvent(_naviMenu, "drag,start,up");
143             _draggedUpCallback.On += (s, e) =>
144             {
145                 if (_footer.TrackObject.IsVisible)
146                 {
147                     Dragged?.Invoke(this, new DraggedEventArgs(DraggedState.EdgeBottom));
148                 }
149                 else
150                 {
151                     Dragged?.Invoke(this, new DraggedEventArgs(DraggedState.Up));
152                 }
153             };
154
155             _draggedDownCallback = new SmartEvent(_naviMenu, "drag,start,down");
156             _draggedDownCallback.On += (s, e) =>
157             {
158                 if (_header.TrackObject.IsVisible)
159                 {
160                     Dragged?.Invoke(this, new DraggedEventArgs(DraggedState.EdgeTop));
161                 }
162                 else
163                 {
164                     Dragged?.Invoke(this, new DraggedEventArgs(DraggedState.Down));
165                 }
166             };
167
168             _outterBox.PackEnd(_naviMenu);
169             _outterBox.PackEnd(_surfaceLayout);
170
171             _surfaceLayout.StackAbove(_naviMenu);
172
173             _defaultClass = new GenItemClass("1icon_1text")
174             {
175                 GetTextHandler = (obj, part) =>
176                 {
177                     if (part == "elm.text")
178                     {
179                         var text = (obj as Item).Text;
180                         if (_foregroundColor != EColor.Default)
181                             return $"<span color='{_foregroundColor.ToHex()}'>{text}</span>";
182                         else
183                             return text;
184                     }
185                     return null;
186                 },
187                 GetContentHandler = (obj, part) =>
188                 {
189                     if (part == "elm.swallow.icon" && obj is Item menuItem && !string.IsNullOrEmpty(menuItem.Icon))
190                     {
191                         var icon = new ElmSharp.Image(Xamarin.Forms.Forms.NativeParent)
192                         {
193                             AlignmentX = -1,
194                             AlignmentY = -1,
195                             WeightX = 1.0,
196                             WeightY = 1.0,
197                             MinimumWidth = _dafaultIconSize,
198                             MinimumHeight = _dafaultIconSize,
199                         };
200                         icon.Show();
201                         icon.Load(menuItem.Icon);
202                         return icon;
203                     }
204                     return null;
205                 }
206             };
207
208             _naviMenu.ItemSelected += OnItemSelected;
209
210         }
211
212         void OnItemSelected(object sender, GenListItemEventArgs e)
213         {
214             ItemSelected?.Invoke(this, new SelectedItemChangedEventArgs((e.Item.Data as Item).Source, -1));
215         }
216
217         void OnLayout()
218         {
219             _surfaceLayout.Geometry = Geometry;
220             _naviMenu.Geometry = Geometry;
221         }
222
223         void UpdateBackgroundColor()
224         {
225             _naviMenu.BackgroundColor = _backgroundColor;
226             foreach (var item in _items)
227             {
228                 item.SetPartColor("bg", _backgroundColor);
229             }
230         }
231
232         void UpdateForegroundColor()
233         {
234             foreach (var item in _items)
235             {
236                 item.Update();
237             }
238         }
239
240         bool IsUpdated(List<List<Element>> items)
241         {
242             if (_itemCache == null)
243                 return true;
244
245             if (_itemCache.Count != items.Count)
246                 return true;
247
248             for (int i = 0; i < items.Count; i++)
249             {
250                 if (_itemCache[i].Count != items[i].Count)
251                     return true;
252
253                 for (int j = 0; j < items[i].Count; j++)
254                 {
255                     if (_itemCache[i][j] != items[i][j])
256                         return true;
257                 }
258             }
259             return false;
260         }
261
262     }
263     public enum DraggedState
264     {
265         EdgeTop,
266         Up,
267         Down,
268         EdgeBottom,
269     }
270
271     public class DraggedEventArgs
272     {
273         public DraggedState State { get; private set; }
274
275         public DraggedEventArgs(DraggedState state)
276         {
277             State = state;
278         }
279     }
280
281     static class FileImageSourceEX
282     {
283         public static string ToAbsPath(this FileImageSource source)
284         {
285             return ResourcePath.GetPath(source.File);
286         }
287     }
288
289     static class ColorEX
290     {
291         public static string ToHex(this EColor c)
292         {
293             return string.Format("#{0:X2}{1:X2}{2:X2}{3:X2}", c.R, c.G, c.B, c.A);
294         }
295     }
296 }