[NUI]Add xaml support for nui and nui xaml test sample (#230)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / XamlBinding / Page.cs
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (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://www.apache.org/licenses/LICENSE-2.0
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 System;
18 using System.Collections.Generic;
19 using System.Collections.ObjectModel;
20 using System.Collections.Specialized;
21 using System.ComponentModel;
22 using System.Linq;
23 using System.Threading;
24 using System.Threading.Tasks;
25 using Tizen.NUI.Internals;
26 using Tizen.NUI.Binding;
27
28 namespace Tizen.NUI
29 {
30         // [RenderWith(typeof(_PageRenderer))]
31         public class Page : /*VisualElement*/BaseHandle, ILayout, IPageController, IElementConfiguration<Page>, IPaddingElement
32         {
33                 public const string BusySetSignalName = "Xamarin.BusySet";
34
35                 public const string AlertSignalName = "Xamarin.SendAlert";
36
37                 public const string ActionSheetSignalName = "Xamarin.ShowActionSheet";
38
39                 internal static readonly BindableProperty IgnoresContainerAreaProperty = BindableProperty.Create("IgnoresContainerArea", typeof(bool), typeof(Page), false);
40
41                 public static readonly BindableProperty BackgroundImageProperty = BindableProperty.Create("BackgroundImage", typeof(string), typeof(Page), default(string));
42
43                 public static readonly BindableProperty IsBusyProperty = BindableProperty.Create("IsBusy", typeof(bool), typeof(Page), false, propertyChanged: (bo, o, n) => ((Page)bo).OnPageBusyChanged());
44
45                 public static readonly BindableProperty PaddingProperty = PaddingElement.PaddingProperty;
46
47                 public static readonly BindableProperty TitleProperty = BindableProperty.Create("Title", typeof(string), typeof(Page), null);
48
49                 public static readonly BindableProperty IconProperty = BindableProperty.Create("Icon", typeof(FileImageSource), typeof(Page), default(FileImageSource));
50
51                 readonly Lazy<PlatformConfigurationRegistry<Page>> _platformConfigurationRegistry;
52
53                 bool _allocatedFlag;
54                 Rectangle _containerArea;
55
56                 bool _containerAreaSet;
57
58                 bool _hasAppeared;
59
60                 ReadOnlyCollection<Element> _logicalChildren;
61
62                 public Page()
63                 {
64                         var toolbarItems = new ObservableCollection<ToolbarItem>();
65                         toolbarItems.CollectionChanged += OnToolbarItemsCollectionChanged;
66                         // ToolbarItems = toolbarItems;
67                         InternalChildren.CollectionChanged += InternalChildrenOnCollectionChanged;
68                         _platformConfigurationRegistry = new Lazy<PlatformConfigurationRegistry<Page>>(() => new PlatformConfigurationRegistry<Page>(this));
69                 }
70
71                 public string BackgroundImage
72                 {
73                         get { return (string)GetValue(BackgroundImageProperty); }
74                         set { SetValue(BackgroundImageProperty, value); }
75                 }
76
77                 internal FileImageSource Icon
78                 {
79                         get { return (FileImageSource)GetValue(IconProperty); }
80                         set { SetValue(IconProperty, value); }
81                 }
82
83                 public bool IsBusy
84                 {
85                         get { return (bool)GetValue(IsBusyProperty); }
86                         set { SetValue(IsBusyProperty, value); }
87                 }
88
89                 public Thickness Padding
90                 {
91                         get { return (Thickness)GetValue(PaddingElement.PaddingProperty); }
92                         set { SetValue(PaddingElement.PaddingProperty, value); }
93                 }
94
95                 Thickness IPaddingElement.PaddingDefaultValueCreator()
96                 {
97                         return default(Thickness);
98                 }
99
100                 void IPaddingElement.OnPaddingPropertyChanged(Thickness oldValue, Thickness newValue)
101                 {
102                         UpdateChildrenLayout();
103                 }
104
105                 public string Title
106                 {
107                         get { return (string)GetValue(TitleProperty); }
108                         set { SetValue(TitleProperty, value); }
109                 }
110
111                 internal IList<ToolbarItem> ToolbarItems { get;/* internal set;*/ }
112
113         [EditorBrowsable(EditorBrowsableState.Never)]
114         public Rectangle ContainerArea
115                 {
116                         get { return _containerArea; }
117                         set
118                         {
119                                 if (_containerArea == value)
120                                         return;
121                                 _containerAreaSet = true;
122                                 _containerArea = value;
123                                 ForceLayout();
124                         }
125                 }
126
127         [EditorBrowsable(EditorBrowsableState.Never)]
128         public bool IgnoresContainerArea
129                 {
130                         get { return (bool)GetValue(IgnoresContainerAreaProperty); }
131                         set { SetValue(IgnoresContainerAreaProperty, value); }
132                 }
133
134         [EditorBrowsable(EditorBrowsableState.Never)]
135         public ObservableCollection<Element> InternalChildren { get; } = new ObservableCollection<Element>();
136
137                 internal override ReadOnlyCollection<Element> LogicalChildrenInternal => 
138                         _logicalChildren ?? (_logicalChildren = new ReadOnlyCollection<Element>(InternalChildren));
139
140                 public event EventHandler LayoutChanged;
141
142                 public event EventHandler Appearing;
143
144                 public event EventHandler Disappearing;
145
146                 public Task<string> DisplayActionSheet(string title, string cancel, string destruction, params string[] buttons)
147                 {
148                         var args = new ActionSheetArguments(title, cancel, destruction, buttons);
149                         MessagingCenter.Send(this, ActionSheetSignalName, args);
150                         return args.Result.Task;
151                 }
152
153                 public Task DisplayAlert(string title, string message, string cancel)
154                 {
155                         return DisplayAlert(title, message, null, cancel);
156                 }
157
158                 public Task<bool> DisplayAlert(string title, string message, string accept, string cancel)
159                 {
160                         if (string.IsNullOrEmpty(cancel))
161                                 throw new ArgumentNullException("cancel");
162
163                         var args = new AlertArguments(title, message, accept, cancel);
164                         MessagingCenter.Send(this, AlertSignalName, args);
165                         return args.Result.Task;
166                 }
167
168                 public void ForceLayout()
169                 {
170                         // SizeAllocated(Width, Height);
171                 }
172
173                 public bool SendBackButtonPressed()
174                 {
175                         return OnBackButtonPressed();
176                 }
177
178                 protected virtual void LayoutChildren(double x, double y, double width, double height)
179                 {
180                         var area = new Rectangle((int)x, (int)y, (int)width, (int)height);
181                         Rectangle originalArea = area;
182                         if (_containerAreaSet)
183                         {
184                                 area = ContainerArea;
185                                 area.X += (int)Padding.Left;
186                                 area.Y += (int)Padding.Right;
187                                 area.Width -= (int)Padding.HorizontalThickness;
188                                 area.Height -= (int)Padding.VerticalThickness;
189                                 area.Width = Math.Max(0, area.Width);
190                                 area.Height = Math.Max(0, area.Height);
191                         }
192
193                         List<Element> elements = LogicalChildren.ToList();
194                         foreach (Element element in elements)
195                         {
196                                 var child = element as /*VisualElement*/BaseHandle;
197                                 if (child == null)
198                                         continue;
199                                 var page = child as Page;
200                                 // if (page != null && page.IgnoresContainerArea)
201                                         // Forms.Layout.LayoutChildIntoBoundingRegion(child, originalArea);
202                                 // else
203                                         // Forms.Layout.LayoutChildIntoBoundingRegion(child, area);
204                         }
205                 }
206
207                 protected virtual void OnAppearing()
208                 {
209                 }
210
211                 protected virtual bool OnBackButtonPressed()
212                 {
213                         var application = RealParent as Application;
214                         // if (application == null || this == application.MainPage)
215                                 // return false;
216
217                         var canceled = false;
218                         EventHandler handler = (sender, args) => { canceled = true; };
219                         // application.PopCanceled += handler;
220                         Navigation.PopModalAsync().ContinueWith(t => { throw t.Exception; }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
221
222                         // application.PopCanceled -= handler;
223                         return !canceled;
224                 }
225
226                 protected override void OnBindingContextChanged()
227                 {
228                         base.OnBindingContextChanged();
229                         foreach (ToolbarItem toolbarItem in ToolbarItems)
230                         {
231                                 SetInheritedBindingContext(toolbarItem, BindingContext);
232                         }
233                 }
234
235                 protected virtual void OnChildMeasureInvalidated(object sender, EventArgs e)
236                 {
237                         InvalidationTrigger trigger = (e as InvalidationEventArgs)?.Trigger ?? InvalidationTrigger.Undefined;
238                         OnChildMeasureInvalidated((/*VisualElement*/BaseHandle)sender, trigger);
239                 }
240
241                 protected virtual void OnDisappearing()
242                 {
243                 }
244
245                 protected override void OnParentSet()
246                 {
247                         //if (!Application.IsApplicationOrNull(RealParent) && !(RealParent is Page))
248                                 // throw new InvalidOperationException("Parent of a Page must also be a Page");
249                         base.OnParentSet();
250                 }
251
252                 // protected override void OnSizeAllocated(double width, double height)
253                 // {
254                 //      _allocatedFlag = true;
255                 //      //base.OnSizeAllocated(width, height);
256                 //      UpdateChildrenLayout();
257                 // }
258
259                 protected void UpdateChildrenLayout()
260                 {
261                         if (!ShouldLayoutChildren())
262                                 return;
263
264                         var startingLayout = new List<Rectangle>(LogicalChildren.Count);
265                         foreach (/*VisualElement*/BaseHandle c in LogicalChildren)
266                         {
267                                 //startingLayout.Add(c.Bounds);
268                         }
269
270                         double x = Padding.Left;
271                         double y = Padding.Top;
272                         //double w = Math.Max(0, Width - Padding.HorizontalThickness);
273                         //double h = Math.Max(0, Height - Padding.VerticalThickness);
274
275                         //LayoutChildren(x, y, w, h);
276
277                         for (var i = 0; i < LogicalChildren.Count; i++)
278                         {
279                                 var c = (/*VisualElement*/BaseHandle)LogicalChildren[i];
280
281                                 // if (c.Bounds != startingLayout[i])
282                                 // {
283                                 //      LayoutChanged?.Invoke(this, EventArgs.Empty);
284                                 //      return;
285                                 // }
286                         }
287                 }
288
289                 internal virtual void OnChildMeasureInvalidated(/*VisualElement*/BaseHandle child, InvalidationTrigger trigger)
290                 {
291                         var container = this as IPageContainer<Page>;
292                         if (container != null)
293                         {
294                                 Page page = container.CurrentPage;
295                                 if (page != null /*&& page.IsVisible && (!page.IsPlatformEnabled || !page.IsNativeStateConsistent)*/)
296                                         return;
297                         }
298                         else
299                         {
300                                 for (var i = 0; i < LogicalChildren.Count; i++)
301                                 {
302                                         var v = LogicalChildren[i] as /*VisualElement*/BaseHandle;
303                                         if (v != null /*&& v.IsVisible && (!v.IsPlatformEnabled || !v.IsNativeStateConsistent)*/)
304                                                 return;
305                                 }
306                         }
307
308                         _allocatedFlag = false;
309                         // InvalidateMeasureInternal(InvalidationTrigger.MeasureChanged);
310                         if (!_allocatedFlag /*&& Width >= 0 && Height >= 0*/)
311                         {
312                                 // SizeAllocated(Width, Height);
313                         }
314                 }
315
316         [EditorBrowsable(EditorBrowsableState.Never)]
317         public void SendAppearing()
318                 {
319                         if (_hasAppeared)
320                                 return;
321
322                         _hasAppeared = true;
323
324                         if (IsBusy)
325                                 MessagingCenter.Send(this, BusySetSignalName, true);
326
327                         OnAppearing();
328                         Appearing?.Invoke(this, EventArgs.Empty);
329
330                         var pageContainer = this as IPageContainer<Page>;
331                         pageContainer?.CurrentPage?.SendAppearing();
332
333                         //FindApplication(this)?.OnPageAppearing(this);
334                 }
335
336         [EditorBrowsable(EditorBrowsableState.Never)]
337         public void SendDisappearing()
338                 {
339                         if (!_hasAppeared)
340                                 return;
341
342                         _hasAppeared = false;
343
344                         if (IsBusy)
345                                 MessagingCenter.Send(this, BusySetSignalName, false);
346
347                         var pageContainer = this as IPageContainer<Page>;
348                         pageContainer?.CurrentPage?.SendDisappearing();
349
350                         OnDisappearing();
351                         Disappearing?.Invoke(this, EventArgs.Empty);
352
353                         //FindApplication(this)?.OnPageDisappearing(this);
354                 }
355
356                 Application FindApplication(Element element)
357                 {
358                         if (element == null)
359                                 return null;
360
361                         return (element.Parent is Application app) ? app : FindApplication(element.Parent);
362                 }
363
364                 void InternalChildrenOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
365                 {
366                         if (e.OldItems != null)
367                         {
368                                 foreach (/*VisualElement*/BaseHandle item in e.OldItems.OfType</*VisualElement*/BaseHandle>())
369                                         OnInternalRemoved(item);
370                         }
371
372                         if (e.NewItems != null)
373                         {
374                                 foreach (/*VisualElement*/BaseHandle item in e.NewItems.OfType</*VisualElement*/BaseHandle>())
375                                         OnInternalAdded(item);
376                         }
377                 }
378
379                 void OnInternalAdded(/*VisualElement*/BaseHandle view)
380                 {
381                         // view.MeasureInvalidated += OnChildMeasureInvalidated;
382
383                         OnChildAdded(view);
384                         // InvalidateMeasureInternal(InvalidationTrigger.MeasureChanged);
385                 }
386
387                 void OnInternalRemoved(/*VisualElement*/BaseHandle view)
388                 {
389                         // view.MeasureInvalidated -= OnChildMeasureInvalidated;
390
391                         OnChildRemoved(view);
392                 }
393
394                 void OnPageBusyChanged()
395                 {
396                         if (!_hasAppeared)
397                                 return;
398
399                         MessagingCenter.Send(this, BusySetSignalName, IsBusy);
400                 }
401
402                 void OnToolbarItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
403                 {
404                         if (args.Action != NotifyCollectionChangedAction.Add)
405                                 return;
406                         foreach (IElement item in args.NewItems)
407                                 item.Parent = this;
408                 }
409
410                 bool ShouldLayoutChildren()
411                 {
412                         if (!LogicalChildren.Any()/* || Width <= 0 || Height <= 0 || !IsNativeStateConsistent*/)
413                                 return false;
414
415                         var container = this as IPageContainer<Page>;
416                         if (container?.CurrentPage != null)
417                         {
418                                 // if (InternalChildren.Contains(container.CurrentPage))
419                                 //      return container.CurrentPage.IsPlatformEnabled && container.CurrentPage.IsNativeStateConsistent;
420                                 return true;
421                         }
422
423                         var any = false;
424                         for (var i = 0; i < LogicalChildren.Count; i++)
425                         {
426                                 var v = LogicalChildren[i] as /*VisualElement*/BaseHandle;
427                                 if (v != null /*&& (!v.IsPlatformEnabled || !v.IsNativeStateConsistent)*/)
428                                 {
429                                         any = true;
430                                         break;
431                                 }
432                         }
433                         return !any;
434                 }
435
436                 public IPlatformElementConfiguration<T, Page> On<T>() where T : IConfigPlatform
437                 {
438                         return _platformConfigurationRegistry.Value.On<T>();
439                 }
440         }
441 }