Support new features of Tizen.CircularUI (#188)
[platform/core/csapi/xsf.git] / src / XSF / Tizen.Wearable.CircularUI.Forms.Renderer / NativeCirclePage.cs
1 /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Flora License, Version 1.1 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://floralicense.org/license/
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 using ElmSharp;
18 using ElmSharp.Wearable;
19 using System;
20 using System.Collections.Generic;
21 using System.ComponentModel;
22 using Xamarin.Forms;
23 using Xamarin.Forms.Platform.Tizen;
24 using Xamarin.Forms.Platform.Tizen.Native;
25 using XForms = Xamarin.Forms.Forms;
26 using XToolbarItem = Xamarin.Forms.ToolbarItem;
27
28 namespace Tizen.Wearable.CircularUI.Forms.Renderer
29 {
30         public class NativeCirclePage : ObservableBox
31         {
32                 ElmSharp.Rectangle _bgColorObject;
33                 ElmSharp.EvasImage _bgImageObject;
34                 ElmSharp.Layout _surfaceLayout;
35                 ElmSharp.Button _actionButton = null;
36                 ImageSource _bgImage;
37
38                 ElmSharp.Wearable.CircleSurface _surface = null;
39                 IRotaryFocusable _currentRotaryFocusObject;
40
41                 ElmSharp.Wearable.MoreOption _moreOption;
42                 Dictionary<XToolbarItem, ElmSharp.Wearable.MoreOptionItem> _toolbarItemMap;
43                 Dictionary<ICircleSurfaceItem, ElmSharp.Wearable.ICircleWidget> _circleSurfaceItems;
44
45                 ActionButtonItem ActionButtonItem;
46                 VisualElement _element = null;
47
48                 public NativeCirclePage(EvasObject parent) : base(parent)
49                 {
50                         SetLayoutCallback(OnLayout);
51
52                         _bgColorObject = new ElmSharp.Rectangle(this)
53                         {
54                                 Color = ElmSharp.Color.Transparent
55                         };
56                         _bgImageObject = new EvasImage(this);
57                         _surfaceLayout = new ElmSharp.Layout(this);
58                         _surface = new ElmSharp.Wearable.CircleSurface(_surfaceLayout);
59
60                         _toolbarItemMap = new Dictionary<XToolbarItem, ElmSharp.Wearable.MoreOptionItem>();
61                         _circleSurfaceItems = new Dictionary<ICircleSurfaceItem, ICircleWidget>();
62
63                         PackEnd(_bgColorObject);
64                         PackEnd(_bgImageObject);
65                         PackEnd(_surfaceLayout);
66
67                         _bgColorObject.Show();
68                         _bgImageObject.Hide();
69                         _surfaceLayout.Show();
70                 }
71
72                 public ElmSharp.Wearable.CircleSurface Surface
73                 {
74                         get { return _surface; }
75                 }
76
77                 public void Dispose(bool disposing)
78                 {
79                         if (ActionButtonItem != null)
80                         {
81                                 ActionButtonItem.PropertyChanged -= OnActionButtonItemChanged;
82                         }
83                 }
84
85                 public void AddToolbarItem(XToolbarItem item)
86                 {
87                         var moreOptionItem = new ActionMoreOptionItem();
88                         var icon = item.IconImageSource;
89                         if (!icon.IsNullOrEmpty())
90                         {
91                                 var iconSource = icon as FileImageSource;
92                                 var img = new ElmSharp.Image(_moreOption);
93                                 img.LoadAsync(ResourcePath.GetPath(iconSource));
94                                 moreOptionItem.Icon = img;
95                         }
96                         var text = item.Text;
97                         if (!string.IsNullOrEmpty(text))
98                         {
99                                 moreOptionItem.MainText = text;
100                         }
101                         if (item is CircleToolbarItem)
102                         {
103                                 var subText = ((CircleToolbarItem)item).SubText;
104                                 if (!string.IsNullOrEmpty(subText))
105                                 {
106                                         moreOptionItem.SubText = subText;
107                                 }
108                         }
109                         moreOptionItem.Action = () => ((IMenuItemController)item).Activate();
110                         _moreOption.Items.Add(moreOptionItem);
111                         _toolbarItemMap[item] = moreOptionItem;
112                 }
113
114                 public void RemoveToolbarITem(XToolbarItem item)
115                 {
116                         if (_toolbarItemMap.TryGetValue(item, out var moreOptionItem))
117                         {
118                                 _moreOption?.Items.Remove(moreOptionItem);
119                                 _toolbarItemMap.Remove(item);
120                         }
121                 }
122
123                 public void AddCircleSurfaceItem(ICircleSurfaceItem item)
124                 {
125                         if (item is CircleProgressBarSurfaceItem)
126                         {
127                                 var widget = new CircleProgressBarSurfaceItemImplements(item as CircleProgressBarSurfaceItem, _surfaceLayout, _surface);
128                                 _circleSurfaceItems[item] = widget;
129                         }
130                         else if (item is CircleSliderSurfaceItem)
131                         {
132                                 var widget = new CircleSliderSurfaceItemImplements(item as CircleSliderSurfaceItem, _surfaceLayout, _surface);
133                                 _circleSurfaceItems[item] = widget;
134                         }
135                 }
136
137                 public void RemoveCircleSurfaceItem(ICircleSurfaceItem item)
138                 {
139                         if (_circleSurfaceItems.TryGetValue(item, out var widget))
140                         {
141                                 ElmSharp.EvasObject obj = widget as ElmSharp.EvasObject;
142                                 obj?.Unrealize();
143                                 _circleSurfaceItems.Remove(item);
144                         }
145                 }
146
147                 public void UpdateRotaryFocusObject(IRotaryFocusable RotaryFocusObject)
148                 {
149                         DeactivateRotaryWidget();
150                         _currentRotaryFocusObject = RotaryFocusObject;
151                         ActivateRotaryWidget();
152                 }
153
154                 public void ActivateRotaryWidget()
155                 {
156                         if (_currentRotaryFocusObject is IRotaryEventReceiver)
157                         {
158                                 RotaryEventManager.Rotated += OnRotaryEventChanged;
159                         }
160                         else if (_currentRotaryFocusObject is IRotaryFocusable)
161                         {
162                                 GetRotaryWidget(_currentRotaryFocusObject)?.Activate();
163                         }
164                 }
165
166                 public void DeactivateRotaryWidget()
167                 {
168                         if (_currentRotaryFocusObject is IRotaryEventReceiver)
169                         {
170                                 RotaryEventManager.Rotated -= OnRotaryEventChanged;
171                         }
172                         else if (_currentRotaryFocusObject is IRotaryFocusable)
173                         {
174                                 GetRotaryWidget(_currentRotaryFocusObject)?.Deactivate();
175                         }
176                 }
177
178                 public void UpdateBackgroundColor(Xamarin.Forms.Color BackgroundColor)
179                 {
180                         if (BackgroundColor.A == 0)
181                         {
182                                 _bgColorObject.Color = ElmSharp.Color.Transparent;
183                         }
184                         else
185                         {
186                                 _bgColorObject.Color = BackgroundColor.ToNative();
187                         }
188                         UpdateBackground();
189                 }
190
191                 public void UpdateBackground()
192                 {
193                         if (_bgImage.IsNullOrEmpty())
194                         {
195                                 _bgImageObject.Hide();
196                         }
197                         else
198                         {
199                                 _bgImageObject.Show();
200                         }
201                 }
202
203                 public void UpdateBackgroundImage(ImageSource BackgroundImageSource)
204                 {
205                         var bgImageSource = BackgroundImageSource as FileImageSource;
206                         if (bgImageSource.IsNullOrEmpty())
207                         {
208                                 _bgImageObject.File = null;
209                                 _bgImage = null;
210                         }
211                         else
212                         {
213                                 _bgImageObject.IsFilled = true;
214                                 _bgImageObject.File = ResourcePath.GetPath(bgImageSource);
215                                 _bgImage = BackgroundImageSource;
216                         }
217                         UpdateBackground();
218                 }
219
220                 public void SetVisibleMoreOption(bool visible)
221                 {
222                         if (_moreOption == null)
223                         {
224                                 _moreOption = new ElmSharp.Wearable.MoreOption(this);
225                                 _moreOption.Clicked += OnMoreOptionClicked;
226                                 _moreOption.Opened += ToolbarOpened;
227                                 _moreOption.Closed += ToolbarClosed;
228                                 PackEnd(_moreOption);
229                         }
230                         if (visible) _moreOption.Show();
231                         else _moreOption.Hide();
232                 }
233
234                 public void UpdateActionButton(ActionButtonItem ActionButton)
235                 {
236                         ActionButtonItem = ActionButton;
237                         if (ActionButton != null)
238                         {
239                                 if (_actionButton == null)
240                                 {
241                                         _actionButton = new ElmSharp.Button(this)
242                                         {
243                                                 Style = "bottom"
244                                         };
245                                         _actionButton.Clicked += OnActionButtonClicked;
246                                         PackEnd(_actionButton);
247                                 }
248
249                                 SetVisibleActionButton(ActionButton.IsVisible);
250
251                                 ActionButton.PropertyChanged += OnActionButtonItemChanged;
252                                 _actionButton.Text = ActionButton.Text?.Replace("&", "&amp;")
253                                                    .Replace("<", "&lt;")
254                                                    .Replace(">", "&gt;")
255                                                    .Replace(Environment.NewLine, "<br>");
256                                 _actionButton.IsEnabled = ActionButton.IsEnable;
257                                 if (!ActionButton.IconImageSource.IsNullOrEmpty())
258                                 {
259                                         var imageSource = ActionButton.IconImageSource as FileImageSource;
260                                         var path = ResourcePath.GetPath(imageSource);
261                                         var buttonImage = new ElmSharp.Image(_actionButton);
262                                         buttonImage.LoadAsync(path);
263                                         buttonImage.Show();
264                                         _actionButton.SetPartContent("elm.swallow.content", buttonImage);
265                                 }
266                                 else
267                                 {
268                                         _actionButton.SetPartContent("elm.swallow.content", null);
269                                 }
270
271                                 _actionButton.BackgroundColor = ActionButton.BackgroundColor.ToNative();
272                         }
273                         else
274                         {
275                                 if (_actionButton != null)
276                                 {
277                                         _actionButton.Clicked -= OnActionButtonClicked;
278                                         UnPack(_actionButton);
279                                         _actionButton.Unrealize();
280                                         _actionButton = null;
281                                 }
282                         }
283                 }
284
285                 internal void SetElement(VisualElement ve)
286                 {
287                         _element = ve;
288                 }
289
290                 void OnLayout()
291                 {
292                         if (_element == null)
293                         {
294                                 return;
295                         }
296                         var rect = Geometry;
297                         _element.Layout(rect.ToDP());
298                         _bgColorObject.Geometry = rect;
299                         _bgImageObject.Geometry = rect;
300
301                         _bgImageObject.StackAbove(_bgColorObject);
302                         EvasObject prev = _bgImageObject;
303
304                         IContainable<EvasObject> container = this;
305                         foreach (var obj in container.Children)
306                         {
307                                 obj.StackAbove(prev);
308                                 prev = obj;
309                         }
310
311                         if (_actionButton != null)
312                         {
313                                 var btnRect = _actionButton.Geometry;
314                                 var btnW = Math.Max(_actionButton.MinimumWidth, btnRect.Width);
315                                 var btnH = Math.Max(_actionButton.MinimumHeight, btnRect.Height);
316                                 var btnX = rect.X + (rect.Width - btnW) / 2;
317                                 var btnY = rect.Y + rect.Height - btnH;
318                                 _actionButton.Geometry = new Rect(btnX, btnY, btnW, btnH);
319                                 _actionButton.StackAbove(prev);
320                                 prev = _actionButton;
321                         }
322
323                         _surfaceLayout.Geometry = rect;
324                         _surfaceLayout.StackAbove(prev);
325                         prev = _surfaceLayout;
326
327                         if (_moreOption != null)
328                         {
329                                 _moreOption.Geometry = rect;
330                                 _moreOption.StackAbove(prev);
331                         }
332                 }
333
334                 void OnRotaryEventChanged(ElmSharp.Wearable.RotaryEventArgs e)
335                 {
336                         if (_currentRotaryFocusObject is IRotaryEventReceiver)
337                         {
338                                 var receiver = _currentRotaryFocusObject as IRotaryEventReceiver;
339                                 receiver.Rotate(new RotaryEventArgs { IsClockwise = e.IsClockwise });
340                         }
341                 }
342
343                 IRotaryActionWidget GetRotaryWidget(IRotaryFocusable focusable)
344                 {
345                         var consumer = focusable as BindableObject;
346                         IRotaryActionWidget rotaryWidget = null;
347                         if (consumer != null)
348                         {
349                                 if (consumer is CircleSliderSurfaceItem)
350                                 {
351                                         ICircleSurfaceItem item = consumer as ICircleSurfaceItem;
352                                         rotaryWidget = GetCircleWidget(item) as IRotaryActionWidget;
353                                 }
354                                 else
355                                 {
356                                         var consumerRenderer = Xamarin.Forms.Platform.Tizen.Platform.GetRenderer(consumer);
357                                         rotaryWidget = consumerRenderer?.NativeView as IRotaryActionWidget;
358                                 }
359                         }
360                         return rotaryWidget;
361                 }
362
363                 ICircleWidget GetCircleWidget(ICircleSurfaceItem item)
364                 {
365                         ElmSharp.Wearable.ICircleWidget widget;
366                         if (_circleSurfaceItems.TryGetValue(item, out widget))
367                         {
368                                 return widget;
369                         }
370                         return null;
371                 }
372
373                 void ToolbarClosed(object sender, EventArgs e)
374                 {
375                         ActivateRotaryWidget();
376                 }
377
378                 void ToolbarOpened(object sender, EventArgs e)
379                 {
380                         DeactivateRotaryWidget();
381                 }
382
383                 void OnMoreOptionClicked(object sender, ElmSharp.Wearable.MoreOptionItemEventArgs e)
384                 {
385                         var item = e.Item as ActionMoreOptionItem;
386                         if (item != null)
387                         {
388                                 item.Action?.Invoke();
389                         }
390                         _moreOption.IsOpened = false;
391                 }
392
393                 void OnActionButtonItemChanged(object sender, PropertyChangedEventArgs e)
394                 {
395                         if (_actionButton == null)
396                         {
397                                 return;
398                         }
399                         if (e.PropertyName == MenuItem.TextProperty.PropertyName)
400                         {
401                                 _actionButton.Text = ActionButtonItem.Text;
402                         }
403                         else if (e.PropertyName == ActionButtonItem.IsEnableProperty.PropertyName)
404                         {
405                                 _actionButton.IsEnabled = ActionButtonItem.IsEnable;
406                         }
407                         else if (e.PropertyName == ActionButtonItem.IsVisibleProperty.PropertyName)
408                         {
409                                 SetVisibleActionButton(ActionButtonItem.IsVisible);
410                         }
411                 }
412
413                 void OnActionButtonClicked(object sender, EventArgs e)
414                 {
415                         if (ActionButtonItem != null)
416                         {
417                                 ((IMenuItemController)ActionButtonItem).Activate();
418                         }
419                 }
420
421                 void SetVisibleActionButton(bool visible)
422                 {
423                         if (_actionButton == null)
424                         {
425                                 return;
426                         }
427                         if (visible) _actionButton.Show();
428                         else _actionButton.Hide();
429                 }
430
431                 class ActionMoreOptionItem : MoreOptionItem
432                 {
433                         public Action Action { get; set; }
434                 }
435         }
436 }