c11ca031859a6e9f2dff628df00ac4d1a9c8effc
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / XamlBinding / Page.cs
1 /*
2  * Copyright (c) 2018 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.Binding.Internals;
26 using Tizen.NUI.Binding;
27
28 namespace Tizen.NUI
29 {
30     /// <summary>
31     /// A BaseHandle that occupies the entire screen.
32     /// </summary>
33     // [RenderWith(typeof(_PageRenderer))]
34     [EditorBrowsable(EditorBrowsableState.Never)]
35     public class Page : /*VisualElement*/BaseHandle, ILayout, IPageController, IElementConfiguration<Page>, IPaddingElement
36     {
37         /// <summary>
38         /// For internal use.
39         /// </summary>
40         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
41         [EditorBrowsable(EditorBrowsableState.Never)]
42         public const string BusySetSignalName = "NUI.BusySet";
43
44         /// <summary>
45         /// For internal use.
46         /// </summary>
47         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
48         [EditorBrowsable(EditorBrowsableState.Never)]
49         public const string AlertSignalName = "NUI.SendAlert";
50
51         /// <summary>
52         /// For internal use.
53         /// </summary>
54         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
55         [EditorBrowsable(EditorBrowsableState.Never)]
56         public const string ActionSheetSignalName = "NUI.ShowActionSheet";
57
58         internal static readonly BindableProperty IgnoresContainerAreaProperty = BindableProperty.Create("IgnoresContainerArea", typeof(bool), typeof(Page), false);
59
60         /// <summary>
61         /// Identifies the BackgroundImage property.
62         /// </summary>
63         internal static readonly BindableProperty BackgroundImageProperty = BindableProperty.Create("BackgroundImage", typeof(string), typeof(Page), default(string));
64
65         /// <summary>
66         /// Identifies the IsBusy property.
67         /// </summary>
68         internal static readonly BindableProperty IsBusyProperty = BindableProperty.Create("IsBusy", typeof(bool), typeof(Page), false, propertyChanged: (bo, o, n) => ((Page)bo).OnPageBusyChanged());
69
70         /// <summary>
71         /// Identifies the Padding property.
72         /// </summary>
73         internal static readonly BindableProperty PaddingProperty = PaddingElement.PaddingProperty;
74
75         /// <summary>
76         /// Identifies the Title property.
77         /// </summary>
78         internal static readonly BindableProperty TitleProperty = BindableProperty.Create("Title", typeof(string), typeof(Page), null);
79
80         /// <summary>
81         /// Identifies the Icon property.
82         /// </summary>
83         internal static readonly BindableProperty IconProperty = BindableProperty.Create("Icon", typeof(FileImageSource), typeof(Page), default(FileImageSource));
84
85         readonly Lazy<PlatformConfigurationRegistry<Page>> _platformConfigurationRegistry;
86
87         Rectangle _containerArea;
88
89         bool _containerAreaSet;
90
91         bool _hasAppeared;
92
93         ReadOnlyCollection<Element> _logicalChildren;
94
95         /// <summary>
96         /// Creates a new Page element with default values.
97         /// </summary>
98         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
99         [EditorBrowsable(EditorBrowsableState.Never)]
100         public Page()
101         {
102             var toolbarItems = new ObservableCollection<ToolbarItem>();
103             toolbarItems.CollectionChanged += OnToolbarItemsCollectionChanged;
104             // ToolbarItems = toolbarItems;
105             InternalChildren.CollectionChanged += InternalChildrenOnCollectionChanged;
106             _platformConfigurationRegistry = new Lazy<PlatformConfigurationRegistry<Page>>(() => new PlatformConfigurationRegistry<Page>(this));
107         }
108
109         /// <summary>
110         /// Identifies the image used as a background for the Page.
111         /// </summary>
112         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
113         [EditorBrowsable(EditorBrowsableState.Never)]
114         public string BackgroundImage
115         {
116             get { return (string)GetValue(BackgroundImageProperty); }
117             set { SetValue(BackgroundImageProperty, value); }
118         }
119
120         internal FileImageSource Icon
121         {
122             get { return (FileImageSource)GetValue(IconProperty); }
123             set { SetValue(IconProperty, value); }
124         }
125
126         /// <summary>
127         /// Marks the Page as busy. This will cause the platform specific global activity indicator to show a busy state.
128         /// </summary>
129         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
130         [EditorBrowsable(EditorBrowsableState.Never)]
131         public bool IsBusy
132         {
133             get { return (bool)GetValue(IsBusyProperty); }
134             set { SetValue(IsBusyProperty, value); }
135         }
136
137         /// <summary>
138         /// The space between the content of the Page and it's border.
139         /// </summary>
140         internal Thickness Padding
141         {
142             get { return (Thickness)GetValue(PaddingElement.PaddingProperty); }
143             set { SetValue(PaddingElement.PaddingProperty, value); }
144         }
145
146         Thickness IPaddingElement.PaddingDefaultValueCreator()
147         {
148             return default(Thickness);
149         }
150
151         void IPaddingElement.OnPaddingPropertyChanged(Thickness oldValue, Thickness newValue)
152         {
153             UpdateChildrenLayout();
154         }
155
156         /// <summary>
157         /// The Page's title.
158         /// </summary>
159         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
160         [EditorBrowsable(EditorBrowsableState.Never)]
161         public string Title
162         {
163             get { return (string)GetValue(TitleProperty); }
164             set { SetValue(TitleProperty, value); }
165         }
166
167         internal IList<ToolbarItem> ToolbarItems { get;/* internal set;*/ }
168
169         /// <summary>
170         /// For internal use.
171         /// </summary>
172         [EditorBrowsable(EditorBrowsableState.Never)]
173         public Rectangle ContainerArea
174         {
175             get { return _containerArea; }
176             set
177             {
178                 if (_containerArea == value)
179                     return;
180                 _containerAreaSet = true;
181                 _containerArea = value;
182                 ForceLayout();
183             }
184         }
185
186         /// <summary>
187         /// For internal use.
188         /// </summary>
189         [EditorBrowsable(EditorBrowsableState.Never)]
190         public bool IgnoresContainerArea
191         {
192             get { return (bool)GetValue(IgnoresContainerAreaProperty); }
193             set { SetValue(IgnoresContainerAreaProperty, value); }
194         }
195
196         /// <summary>
197         /// For internal use.
198         /// </summary>
199         [EditorBrowsable(EditorBrowsableState.Never)]
200         public ObservableCollection<Element> InternalChildren { get; } = new ObservableCollection<Element>();
201
202         internal override ReadOnlyCollection<Element> LogicalChildrenInternal =>
203             _logicalChildren ?? (_logicalChildren = new ReadOnlyCollection<Element>(InternalChildren));
204
205         /// <summary>
206         /// Raised when the layout of the Page has changed.
207         /// </summary>
208         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
209         [EditorBrowsable(EditorBrowsableState.Never)]
210         public event EventHandler LayoutChanged;
211
212         /// <summary>
213         /// ndicates that the Page is about to appear.
214         /// </summary>
215         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
216         [EditorBrowsable(EditorBrowsableState.Never)]
217         public event EventHandler Appearing;
218
219         /// <summary>
220         /// Indicates that the Page is about to cease displaying.
221         /// </summary>
222         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
223         [EditorBrowsable(EditorBrowsableState.Never)]
224         public event EventHandler Disappearing;
225
226         /// <summary>
227         /// Displays a native platform action sheet, allowing the application user to choose from several buttons.
228         /// </summary>
229         /// <param name="title">Title of the displayed action sheet. Must not be null.</param>
230         /// <param name="cancel">Text to be displayed in the 'Cancel' button. Can be null to hide the cancel action.</param>
231         /// <param name="destruction">Text to be displayed in the 'Destruct' button. Can be null to hide the destructive option.</param>
232         /// <param name="buttons">Text labels for additional buttons. Must not be null.</param>
233         /// <returns>An awaitable Task that displays an action sheet and returns the Text of the button pressed by the user.</returns>
234         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
235         [EditorBrowsable(EditorBrowsableState.Never)]
236         public Task<string> DisplayActionSheet(string title, string cancel, string destruction, params string[] buttons)
237         {
238             var args = new ActionSheetArguments(title, cancel, destruction, buttons);
239             MessagingCenter.Send(this, ActionSheetSignalName, args);
240             return args.Result.Task;
241         }
242
243         /// <summary>
244         /// Presents an alert dialog to the application user with a single cancel button.
245         /// </summary>
246         /// <param name="title">The title of the alert dialog.</param>
247         /// <param name="message">The body text of the alert dialog.</param>
248         /// <param name="cancel">Text to be displayed on the 'Cancel' button.</param>
249         /// <returns></returns>
250         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
251         [EditorBrowsable(EditorBrowsableState.Never)]
252         public Task DisplayAlert(string title, string message, string cancel)
253         {
254             return DisplayAlert(title, message, null, cancel);
255         }
256
257         /// <summary>
258         /// resents an alert dialog to the application user with an accept and a cancel button.
259         /// </summary>
260         /// <param name="title">The title of the alert dialog.</param>
261         /// <param name="message">The body text of the alert dialog.</param>
262         /// <param name="accept">Text to be displayed on the 'Accept' button.</param>
263         /// <param name="cancel">Text to be displayed on the 'Cancel' button.</param>
264         /// <returns></returns>
265         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
266         [EditorBrowsable(EditorBrowsableState.Never)]
267         public Task<bool> DisplayAlert(string title, string message, string accept, string cancel)
268         {
269             if (string.IsNullOrEmpty(cancel))
270                 throw new ArgumentNullException("cancel");
271
272             var args = new AlertArguments(title, message, accept, cancel);
273             MessagingCenter.Send(this, AlertSignalName, args);
274             return args.Result.Task;
275         }
276
277         /// <summary>
278         /// Forces the Page to perform a layout pass.
279         /// </summary>
280         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
281         [EditorBrowsable(EditorBrowsableState.Never)]
282         public void ForceLayout()
283         {
284         }
285
286         /// <summary>
287         /// Calls OnBackButtonPressed().
288         /// </summary>
289         /// <returns></returns>
290         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
291         [EditorBrowsable(EditorBrowsableState.Never)]
292         public bool SendBackButtonPressed()
293         {
294             return OnBackButtonPressed();
295         }
296
297         /// <summary>
298         /// Lays out children Elements into the specified area.
299         /// </summary>
300         /// <param name="x">Left-hand side of layout area.</param>
301         /// <param name="y">Top of layout area.</param>
302         /// <param name="width">Width of layout area.</param>
303         /// <param name="height">Height of layout area.</param>
304         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
305         [EditorBrowsable(EditorBrowsableState.Never)]
306         protected virtual void LayoutChildren(double x, double y, double width, double height)
307         {
308             var area = new Rectangle((int)x, (int)y, (int)width, (int)height);
309             Rectangle originalArea = area;
310             if (_containerAreaSet)
311             {
312                 area = ContainerArea;
313                 area.X += (int)Padding.Left;
314                 area.Y += (int)Padding.Right;
315                 area.Width -= (int)Padding.HorizontalThickness;
316                 area.Height -= (int)Padding.VerticalThickness;
317                 area.Width = Math.Max(0, area.Width);
318                 area.Height = Math.Max(0, area.Height);
319             }
320         }
321
322         /// <summary>
323         /// When overridden, allows application developers to customize behavior immediately prior to the Page becoming visible.
324         /// </summary>
325         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
326         [EditorBrowsable(EditorBrowsableState.Never)]
327         protected virtual void OnAppearing()
328         {
329         }
330
331         /// <summary>
332         /// Application developers can override this method to provide behavior when the back button is pressed.
333         /// </summary>
334         /// <returns>true if consumed</returns>
335         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
336         [EditorBrowsable(EditorBrowsableState.Never)]
337         protected virtual bool OnBackButtonPressed()
338         {
339             var application = RealParent as Application;
340
341             var canceled = false;
342             EventHandler handler = (sender, args) => { canceled = true; };
343             Navigation.PopModalAsync().ContinueWith(t => { throw t.Exception; }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
344
345             return !canceled;
346         }
347
348         /// <summary>
349         /// Invoked whenever the binding context of the Page changes. Override this method to add class handling for this event.
350         /// </summary>
351         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
352         [EditorBrowsable(EditorBrowsableState.Never)]
353         protected override void OnBindingContextChanged()
354         {
355             base.OnBindingContextChanged();
356         }
357
358         /// <summary>
359         /// Indicates that the preferred size of a child Element has changed.
360         /// </summary>
361         /// <param name="sender">The object that raised the event.</param>
362         /// <param name="e">The event arguments.</param>
363         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
364         [EditorBrowsable(EditorBrowsableState.Never)]
365         protected virtual void OnChildMeasureInvalidated(object sender, EventArgs e)
366         {
367             InvalidationTrigger trigger = (e as InvalidationEventArgs)?.Trigger ?? InvalidationTrigger.Undefined;
368             OnChildMeasureInvalidated((BaseHandle)sender, trigger);
369         }
370
371         /// <summary>
372         /// When overridden, allows the application developer to customize behavior as the Page disappears.
373         /// </summary>
374         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
375         [EditorBrowsable(EditorBrowsableState.Never)]
376         protected virtual void OnDisappearing()
377         {
378         }
379
380         /// <summary>
381         /// Called when the Page's Parent property has changed.
382         /// </summary>
383         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
384         [EditorBrowsable(EditorBrowsableState.Never)]
385         protected override void OnParentSet()
386         {
387             base.OnParentSet();
388         }
389
390         /// <summary>
391         /// Requests that the children Elements of the Page update their layouts.
392         /// </summary>
393         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
394         [EditorBrowsable(EditorBrowsableState.Never)]
395         protected void UpdateChildrenLayout()
396         {
397             if (!ShouldLayoutChildren())
398                 return;
399
400             double x = Padding.Left;
401             double y = Padding.Top;
402
403             for (var i = 0; i < LogicalChildren.Count; i++)
404             {
405                 LayoutChanged?.Invoke(this, EventArgs.Empty);
406             }
407         }
408
409         internal virtual void OnChildMeasureInvalidated(BaseHandle child, InvalidationTrigger trigger)
410         {
411             var container = this as IPageContainer<Page>;
412             if (container != null)
413             {
414                 Page page = container.CurrentPage;
415                 if (page != null)
416                 {
417                     return;
418                 }
419             }
420             else
421             {
422                 for (var i = 0; i < LogicalChildren.Count; i++)
423                 {
424                     var v = LogicalChildren[i] as BaseHandle;
425                     if (v != null)
426                     {
427                         return;
428                     }
429                 }
430             }
431         }
432
433         /// <summary>
434         /// For intarnal use.
435         /// </summary>
436         [EditorBrowsable(EditorBrowsableState.Never)]
437         public void SendAppearing()
438         {
439             if (_hasAppeared)
440                 return;
441
442             _hasAppeared = true;
443
444             if (IsBusy)
445                 MessagingCenter.Send(this, BusySetSignalName, true);
446
447             OnAppearing();
448             Appearing?.Invoke(this, EventArgs.Empty);
449
450             var pageContainer = this as IPageContainer<Page>;
451             pageContainer?.CurrentPage?.SendAppearing();
452         }
453
454         /// <summary>
455         /// For intarnal use.
456         /// </summary>
457         [EditorBrowsable(EditorBrowsableState.Never)]
458         public void SendDisappearing()
459         {
460             if (!_hasAppeared)
461                 return;
462
463             _hasAppeared = false;
464
465             if (IsBusy)
466                 MessagingCenter.Send(this, BusySetSignalName, false);
467
468             var pageContainer = this as IPageContainer<Page>;
469             pageContainer?.CurrentPage?.SendDisappearing();
470
471             OnDisappearing();
472             Disappearing?.Invoke(this, EventArgs.Empty);
473         }
474
475         Application FindApplication(Element element)
476         {
477             if (element == null)
478                 return null;
479
480             return (element.Parent is Application app) ? app : FindApplication(element.Parent);
481         }
482
483         void InternalChildrenOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
484         {
485             if (e.OldItems != null)
486             {
487                 foreach (BaseHandle item in e.OldItems.OfType<BaseHandle>())
488                 {
489                     OnInternalRemoved(item);
490                 }
491             }
492
493             if (e.NewItems != null)
494             {
495                 foreach (BaseHandle item in e.NewItems.OfType<BaseHandle>())
496                 {
497                     OnInternalAdded(item);
498                 }
499             }
500         }
501
502         private void OnInternalAdded(BaseHandle view)
503         {
504             OnChildAdded(view);
505         }
506
507         private void OnInternalRemoved(BaseHandle view)
508         {
509             OnChildRemoved(view);
510         }
511
512         void OnPageBusyChanged()
513         {
514             if (!_hasAppeared)
515                 return;
516
517             MessagingCenter.Send(this, BusySetSignalName, IsBusy);
518         }
519
520         void OnToolbarItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
521         {
522             if (args.Action != NotifyCollectionChangedAction.Add)
523                 return;
524             foreach (IElement item in args.NewItems)
525                 item.Parent = this;
526         }
527
528         bool ShouldLayoutChildren()
529         {
530             if (!LogicalChildren.Any())
531             {
532                 return false;
533             }
534
535             var container = this as IPageContainer<Page>;
536             if (container?.CurrentPage != null)
537             {
538                 return true;
539             }
540
541             var any = false;
542             for (var i = 0; i < LogicalChildren.Count; i++)
543             {
544                 var v = LogicalChildren[i] as BaseHandle;
545                 if (v != null)
546                 {
547                     any = true;
548                     break;
549                 }
550             }
551             return !any;
552         }
553
554         /// <summary>
555         /// Returns the platform-specific instance of this Page, on which a platform-specific method may be called.
556         /// </summary>
557         /// <typeparam name="T">The platform for which to return an instance.</typeparam>
558         /// <returns>The platform-specific instance of this Page</returns>
559         internal IPlatformElementConfiguration<T, Page> On<T>() where T : IConfigPlatform
560         {
561             return _platformConfigurationRegistry.Value.On<T>();
562         }
563     }
564 }