0f9d047281c553cc38fee2fbb8b7899d993cf69b
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / Navigation / Navigator.cs
1 /*
2  * Copyright(c) 2021 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
18 using System;
19 using System.Collections.Generic;
20 using System.ComponentModel;
21 using Tizen.NUI.BaseComponents;
22 using Tizen.NUI.Binding;
23
24 namespace Tizen.NUI.Components
25 {
26     /// <summary>
27     /// PoppedEventArgs is a class to record <see cref="Navigator.Popped"/> event arguments which will be sent to user.
28     /// </summary>
29     /// <since_tizen> 9 </since_tizen>
30     public class PoppedEventArgs : EventArgs
31     {
32         /// <summary>
33         /// Page popped by Navigator.
34         /// </summary>
35         /// <since_tizen> 9 </since_tizen>
36         public Page Page { get; internal set; }
37     }
38
39     /// <summary>
40     /// The Navigator is a class which navigates pages with stack methods such as Push and Pop.
41     /// </summary>
42     /// <remarks>
43     /// With Transition class, Navigator supports smooth transition of View pair between two Pages
44     /// by using <see cref="PushWithTransition(Page)"/> and <see cref="PopWithTransition()"/> methods.
45     /// If current top Page and next top Page have <see cref="View"/>s those have same TransitionTag,
46     /// Navigator creates smooth transition motion for them.
47     /// Navigator.Transition property can be used to set properties of the Transition such as TimePeriod and AlphaFunction.
48     /// When all transitions are finished, Navigator calls a callback methods those connected on the "TransitionFinished" event.
49     /// </remarks>
50     /// <example>
51     /// <code>
52     /// Navigator navigator = new Navigator()
53     /// {
54     ///     TimePeriod = new TimePeriod(500),
55     ///     AlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.EaseInOutSine)
56     /// };
57     ///
58     /// View view = new View()
59     /// {
60     ///     TransitionOptions = new TransitionOptions()
61     ///     {
62     ///         /* Set properties for the transition of this View */
63     ///     }
64     /// };
65     ///
66     /// ContentPage newPage = new ContentPage()
67     /// {
68     ///     Content = view,
69     /// };
70     ///
71     /// Navigator.PushWithTransition(newPage);
72     /// </code>
73     /// </example>
74     /// <since_tizen> 9 </since_tizen>
75     public class Navigator : Control
76     {
77         /// <summary>
78         /// TransitionProperty
79         /// </summary>
80         [EditorBrowsable(EditorBrowsableState.Never)]
81         public static readonly BindableProperty TransitionProperty = BindableProperty.Create(nameof(Transition), typeof(Transition), typeof(Navigator), null, propertyChanged: (bindable, oldValue, newValue) =>
82         {
83             var instance = (Navigator)bindable;
84             if (newValue != null)
85             {
86                 instance.InternalTransition = newValue as Transition;
87             }
88         },
89         defaultValueCreator: (bindable) =>
90         {
91             var instance = (Navigator)bindable;
92             return instance.InternalTransition;
93         });
94
95         /// <summary>
96         /// EnableBackNavigationProperty
97         /// </summary>
98         [EditorBrowsable(EditorBrowsableState.Never)]
99         public static readonly BindableProperty EnableBackNavigationProperty = BindableProperty.Create(nameof(EnableBackNavigation), typeof(bool), typeof(Navigator), default(bool), propertyChanged: (bindable, oldValue, newValue) =>
100         {
101             var instance = (Navigator)bindable;
102             if (newValue != null)
103             {
104                 instance.InternalEnableBackNavigation = (bool)newValue;
105             }
106         },
107         defaultValueCreator: (bindable) =>
108         {
109             var instance = (Navigator)bindable;
110             return instance.InternalEnableBackNavigation;
111         });
112
113         private const int DefaultTransitionDuration = 300;
114
115         //This will be replaced with view transition class instance.
116         private Animation curAnimation = null;
117
118         //This will be replaced with view transition class instance.
119         private Animation newAnimation = null;
120
121         private TransitionSet transitionSet = null;
122
123         private Transition transition = new Transition()
124         {
125             TimePeriod = new TimePeriod(DefaultTransitionDuration),
126             AlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.Default),
127         };
128
129         private bool transitionFinished = true;
130
131         //TODO: Needs to consider how to remove disposed window from dictionary.
132         //Two dictionaries are required to remove disposed navigator from dictionary.
133         private static Dictionary<Window, Navigator> windowNavigator = new Dictionary<Window, Navigator>();
134         private static Dictionary<Navigator, Window> navigatorWindow = new Dictionary<Navigator, Window>();
135
136         private List<Page> navigationPages = new List<Page>();
137
138         private bool enableBackNavigation = true;
139
140         private Window parentWindow;
141
142         private void OnWindowKeyEvent(object sender, Window.KeyEventArgs e)
143         {
144             if (!EnableBackNavigation)
145             {
146                 return;
147             }
148
149             if ((e.Key.State == Key.StateType.Up) && ((e.Key.KeyPressedName == "Escape") || (e.Key.KeyPressedName == "BackSpace") || (e.Key.KeyPressedName == "XF86Back")))
150             {
151                 OnBackNavigation(new BackNavigationEventArgs());
152             }
153         }
154
155         private void OnAddedToWindow(object sender, EventArgs e)
156         {
157             parentWindow = Window.Get(this);
158             parentWindow.KeyEvent += OnWindowKeyEvent;
159         }
160
161         private void OnRemovedFromWindow(object sender, EventArgs e)
162         {
163             parentWindow.KeyEvent -= OnWindowKeyEvent;
164             parentWindow = null;
165         }
166
167         private void Initialize()
168         {
169             Layout = new AbsoluteLayout();
170
171             AddedToWindow += OnAddedToWindow;
172             RemovedFromWindow += OnRemovedFromWindow;
173         }
174
175         /// <summary>
176         /// Creates a new instance of a Navigator.
177         /// </summary>
178         /// <since_tizen> 9 </since_tizen>
179         public Navigator() : base()
180         {
181             Initialize();
182         }
183
184         /// <summary>
185         /// Creates a new instance of a Navigator with style.
186         /// </summary>
187         /// <param name="style">A style applied to the newly created Navigator.</param>
188         [EditorBrowsable(EditorBrowsableState.Never)]
189         public Navigator(ControlStyle style) : base(style)
190         {
191             Initialize();
192         }
193
194         /// <inheritdoc/>
195         [EditorBrowsable(EditorBrowsableState.Never)]
196         public override void OnInitialize()
197         {
198             base.OnInitialize();
199
200             AccessibilityRole = Role.PageTabList;
201         }
202
203         /// <summary>
204         /// An event fired when Transition has been finished.
205         /// </summary>
206         /// <since_tizen> 9 </since_tizen>
207         public event EventHandler<EventArgs> TransitionFinished;
208
209         /// <summary>
210         /// An event fired when Pop of a page has been finished.
211         /// </summary>
212         /// <remarks>
213         /// When you free resources in the Popped event handler, please make sure if the popped page is the page you find.
214         /// </remarks>
215         /// <since_tizen> 9 </since_tizen>
216         public event EventHandler<PoppedEventArgs> Popped;
217
218         /// <summary>
219         /// Returns the count of pages in Navigator.
220         /// </summary>
221         /// <since_tizen> 9 </since_tizen>
222         public int PageCount => navigationPages.Count;
223
224         /// <summary>
225         /// Transition properties for the transition of View pair having same transition tag.
226         /// </summary>
227         /// <since_tizen> 9 </since_tizen>
228         public Transition Transition
229         {
230             get
231             {
232                 return GetValue(TransitionProperty) as Transition;
233             }
234             set
235             {
236                 SetValue(TransitionProperty, value);
237                 NotifyPropertyChanged();
238             }
239         }
240         private Transition InternalTransition
241         {
242             set
243             {
244                 transition = value;
245             }
246             get
247             {
248                 return transition;
249             }
250         }
251
252         /// <summary>
253         /// Pushes a page to Navigator.
254         /// If the page is already in Navigator, then it is not pushed.
255         /// </summary>
256         /// <param name="page">The page to push to Navigator.</param>
257         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
258         /// <since_tizen> 9 </since_tizen>
259         public void PushWithTransition(Page page)
260         {
261             if (!transitionFinished)
262             {
263                 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
264                 return;
265             }
266
267             if (page == null)
268             {
269                 throw new ArgumentNullException(nameof(page), "page should not be null.");
270             }
271
272             //Duplicate page is not pushed.
273             if (navigationPages.Contains(page)) return;
274
275             var topPage = Peek();
276
277             if (!topPage)
278             {
279                 Insert(0, page);
280                 return;
281             }
282
283             navigationPages.Add(page);
284             Add(page);
285             page.Navigator = this;
286
287             //Invoke Page events
288             page.InvokeAppearing();
289             topPage.InvokeDisappearing();
290             topPage.SaveKeyFocus();
291
292             transitionSet = CreateTransitions(topPage, page, true);
293             transitionSet.Finished += (object sender, EventArgs e) =>
294             {
295                 if (page is DialogPage == false)
296                 {
297                     topPage.SetVisible(false);
298                 }
299
300                 // Need to update Content of the new page
301                 ShowContentOfPage(page);
302
303                 //Invoke Page events
304                 page.InvokeAppeared();
305                 page.RestoreKeyFocus();
306                 
307                 topPage.InvokeDisappeared();
308                 NotifyAccessibilityStatesChangeOfPages(topPage, page);
309             };
310             transitionFinished = false;
311         }
312
313         /// <summary>
314         /// Pops the top page from Navigator.
315         /// </summary>
316         /// <returns>The popped page.</returns>
317         /// <exception cref="InvalidOperationException">Thrown when there is no page in Navigator.</exception>
318         /// <since_tizen> 9 </since_tizen>
319         public Page PopWithTransition()
320         {
321             if (!transitionFinished)
322             {
323                 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
324                 return null;
325             }
326
327             if (navigationPages.Count == 0)
328             {
329                 throw new InvalidOperationException("There is no page in Navigator.");
330             }
331
332             var topPage = Peek();
333
334             if (navigationPages.Count == 1)
335             {
336                 Remove(topPage);
337
338                 //Invoke Popped event
339                 Popped?.Invoke(this, new PoppedEventArgs() { Page = topPage });
340
341                 return topPage;
342             }
343             var newTopPage = navigationPages[navigationPages.Count - 2];
344
345             //Invoke Page events
346             newTopPage.InvokeAppearing();
347             topPage.InvokeDisappearing();
348             topPage.SaveKeyFocus();
349
350             transitionSet = CreateTransitions(topPage, newTopPage, false);
351             transitionSet.Finished += (object sender, EventArgs e) =>
352             {
353                 Remove(topPage);
354                 topPage.SetVisible(true);
355
356                 // Need to update Content of the new page
357                 ShowContentOfPage(newTopPage);
358
359                 //Invoke Page events
360                 newTopPage.InvokeAppeared();
361                 newTopPage.RestoreKeyFocus();
362
363                 topPage.InvokeDisappeared();
364
365                 //Invoke Popped event
366                 Popped?.Invoke(this, new PoppedEventArgs() { Page = topPage });
367             };
368             transitionFinished = false;
369
370             return topPage;
371         }
372
373         /// <summary>
374         /// Pushes a page to Navigator.
375         /// If the page is already in Navigator, then it is not pushed.
376         /// </summary>
377         /// <param name="page">The page to push to Navigator.</param>
378         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
379         /// <since_tizen> 9 </since_tizen>
380         public void Push(Page page)
381         {
382             if (!transitionFinished)
383             {
384                 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
385                 return;
386             }
387
388             if (page == null)
389             {
390                 throw new ArgumentNullException(nameof(page), "page should not be null.");
391             }
392
393             //Duplicate page is not pushed.
394             if (navigationPages.Contains(page)) return;
395
396             var curTop = Peek();
397
398             if (!curTop)
399             {
400                 Insert(0, page);
401                 return;
402             }
403
404             navigationPages.Add(page);
405             Add(page);
406             page.Navigator = this;
407
408             //Invoke Page events
409             page.InvokeAppearing();
410             curTop.InvokeDisappearing();
411
412             curTop.SaveKeyFocus();
413
414             //TODO: The following transition codes will be replaced with view transition.
415             InitializeAnimation();
416
417             if (page is DialogPage == false)
418             {
419                 curAnimation = new Animation(DefaultTransitionDuration);
420                 curAnimation.AnimateTo(curTop, "PositionX", 0.0f, 0, DefaultTransitionDuration);
421                 curAnimation.EndAction = Animation.EndActions.StopFinal;
422                 curAnimation.Finished += (object sender, EventArgs args) =>
423                 {
424                     curTop.SetVisible(false);
425
426                     //Invoke Page events
427                     curTop.InvokeDisappeared();
428                 };
429                 curAnimation.Play();
430
431                 page.PositionX = SizeWidth;
432                 page.SetVisible(true);
433                 // Set Content visible because it was hidden by HideContentOfPage.
434                 (page as ContentPage)?.Content?.SetVisible(true);
435
436                 newAnimation = new Animation(DefaultTransitionDuration);
437                 newAnimation.AnimateTo(page, "PositionX", 0.0f, 0, DefaultTransitionDuration);
438                 newAnimation.EndAction = Animation.EndActions.StopFinal;
439                 newAnimation.Finished += (object sender, EventArgs e) =>
440                 {
441                     // Need to update Content of the new page
442                     ShowContentOfPage(page);
443
444                     //Invoke Page events
445                     page.InvokeAppeared();
446                     NotifyAccessibilityStatesChangeOfPages(curTop, page);
447
448                     page.RestoreKeyFocus();
449                 };
450                 newAnimation.Play();
451             }
452             else
453             {
454                 ShowContentOfPage(page);
455                 page.RestoreKeyFocus();
456             }
457         }
458
459         /// <summary>
460         /// Pops the top page from Navigator.
461         /// </summary>
462         /// <returns>The popped page.</returns>
463         /// <exception cref="InvalidOperationException">Thrown when there is no page in Navigator.</exception>
464         /// <since_tizen> 9 </since_tizen>
465         public Page Pop()
466         {
467             if (!transitionFinished)
468             {
469                 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
470                 return null;
471             }
472
473             if (navigationPages.Count == 0)
474             {
475                 throw new InvalidOperationException("There is no page in Navigator.");
476             }
477
478             var curTop = Peek();
479
480             if (navigationPages.Count == 1)
481             {
482                 Remove(curTop);
483
484                 //Invoke Popped event
485                 Popped?.Invoke(this, new PoppedEventArgs() { Page = curTop });
486
487                 return curTop;
488             }
489
490             var newTop = navigationPages[navigationPages.Count - 2];
491
492             //Invoke Page events
493             newTop.InvokeAppearing();
494             curTop.InvokeDisappearing();
495             curTop.SaveKeyFocus();
496
497             //TODO: The following transition codes will be replaced with view transition.
498             InitializeAnimation();
499
500             if (curTop is DialogPage == false)
501             {
502                 curAnimation = new Animation(DefaultTransitionDuration);
503                 curAnimation.AnimateTo(curTop, "PositionX", SizeWidth, 0, DefaultTransitionDuration);
504                 curAnimation.EndAction = Animation.EndActions.StopFinal;
505                 curAnimation.Finished += (object sender, EventArgs e) =>
506                 {
507                     //Removes the current top page after transition is finished.
508                     Remove(curTop);
509
510                     curTop.PositionX = 0.0f;
511
512                     //Invoke Page events
513                     curTop.InvokeDisappeared();
514
515                     //Invoke Popped event
516                     Popped?.Invoke(this, new PoppedEventArgs() { Page = curTop });
517                 };
518                 curAnimation.Play();
519
520                 newTop.SetVisible(true);
521                 // Set Content visible because it was hidden by HideContentOfPage.
522                 (newTop as ContentPage)?.Content?.SetVisible(true);
523
524                 newAnimation = new Animation(DefaultTransitionDuration);
525                 newAnimation.AnimateTo(newTop, "PositionX", 0.0f, 0, DefaultTransitionDuration);
526                 newAnimation.EndAction = Animation.EndActions.StopFinal;
527                 newAnimation.Finished += (object sender, EventArgs e) =>
528                 {
529                     // Need to update Content of the new page
530                     ShowContentOfPage(newTop);
531
532                     //Invoke Page events
533                     newTop.InvokeAppeared();
534
535                     newTop.RestoreKeyFocus();
536                 };
537                 newAnimation.Play();
538             }
539             else
540             {
541                 Remove(curTop);
542                 newTop.RestoreKeyFocus();
543             }
544
545             return curTop;
546         }
547
548         /// <summary>
549         /// Returns the page of the given index in Navigator.
550         /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
551         /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
552         /// </summary>
553         /// <param name="index">The index of a page in Navigator.</param>
554         /// <returns>The page of the given index in Navigator.</returns>
555         /// <exception cref="ArgumentOutOfRangeException">Thrown when the argument index is less than 0, or greater than the number of pages.</exception>
556         public Page GetPage(int index)
557         {
558             if ((index < 0) || (index > navigationPages.Count))
559             {
560                 throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than or equal to the number of pages.");
561             }
562
563             return navigationPages[index];
564         }
565
566         /// <summary>
567         /// Returns the current index of the given page in Navigator.
568         /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
569         /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
570         /// </summary>
571         /// <param name="page">The page in Navigator.</param>
572         /// <returns>The index of the given page in Navigator. If the given page is not in the Navigator, then -1 is returned.</returns>
573         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
574         /// <since_tizen> 9 </since_tizen>
575         public int IndexOf(Page page)
576         {
577             if (page == null)
578             {
579                 throw new ArgumentNullException(nameof(page), "page should not be null.");
580             }
581
582             for (int i = 0; i < navigationPages.Count; i++)
583             {
584                 if (navigationPages[i] == page)
585                 {
586                     return i;
587                 }
588             }
589
590             return -1;
591         }
592
593         /// <summary>
594         /// Inserts a page at the specified index of Navigator.
595         /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
596         /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
597         /// To find the current index of a page in Navigator, please use IndexOf(page).
598         /// If the page is already in Navigator, then it is not inserted.
599         /// </summary>
600         /// <param name="index">The index of a page in Navigator where the page will be inserted.</param>
601         /// <param name="page">The page to insert to Navigator.</param>
602         /// <exception cref="ArgumentOutOfRangeException">Thrown when the argument index is less than 0, or greater than the number of pages.</exception>
603         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
604         /// <since_tizen> 9 </since_tizen>
605         public void Insert(int index, Page page)
606         {
607             if ((index < 0) || (index > navigationPages.Count))
608             {
609                 throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than or equal to the number of pages.");
610             }
611
612             if (page == null)
613             {
614                 throw new ArgumentNullException(nameof(page), "page should not be null.");
615             }
616
617             //Duplicate page is not pushed.
618             if (navigationPages.Contains(page)) return;
619
620             //TODO: The following transition codes will be replaced with view transition.
621             InitializeAnimation();
622
623             ShowContentOfPage(page);
624
625             if (index == PageCount)
626             {
627                 page.SetVisible(true);
628             }
629             else
630             {
631                 page.SetVisible(false);
632             }
633
634             navigationPages.Insert(index, page);
635             Add(page);
636             page.SiblingOrder = index;
637             page.Navigator = this;
638             if (index == PageCount - 1)
639             {
640                 if (PageCount > 1)
641                 {
642                     NotifyAccessibilityStatesChangeOfPages(navigationPages[PageCount - 2], page);
643                 }
644                 else
645                 {
646                     NotifyAccessibilityStatesChangeOfPages(null, page);
647                 }
648             }
649         }
650
651         /// <summary>
652         /// Inserts a page to Navigator before an existing page.
653         /// If the page is already in Navigator, then it is not inserted.
654         /// </summary>
655         /// <param name="before">The existing page, before which a page will be inserted.</param>
656         /// <param name="page">The page to insert to Navigator.</param>
657         /// <exception cref="ArgumentNullException">Thrown when the argument before is null.</exception>
658         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
659         /// <exception cref="ArgumentException">Thrown when the argument before does not exist in Navigator.</exception>
660         /// <since_tizen> 9 </since_tizen>
661         public void InsertBefore(Page before, Page page)
662         {
663             if (before == null)
664             {
665                 throw new ArgumentNullException(nameof(before), "before should not be null.");
666             }
667
668             if (page == null)
669             {
670                 throw new ArgumentNullException(nameof(page), "page should not be null.");
671             }
672
673             //Find the index of before page.
674             int beforeIndex = navigationPages.FindIndex(x => x == before);
675
676             //before does not exist in Navigator.
677             if (beforeIndex == -1)
678             {
679                 throw new ArgumentException("before does not exist in Navigator.", nameof(before));
680             }
681
682             Insert(beforeIndex, page);
683         }
684
685         /// <summary>
686         /// Removes a page from Navigator.
687         /// </summary>
688         /// <param name="page">The page to remove from Navigator.</param>
689         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
690         /// <since_tizen> 9 </since_tizen>
691         public void Remove(Page page)
692         {
693             if (page == null)
694             {
695                 throw new ArgumentNullException(nameof(page), "page should not be null.");
696             }
697
698             //TODO: The following transition codes will be replaced with view transition.
699             InitializeAnimation();
700
701             HideContentOfPage(page);
702
703             if (page == Peek())
704             {
705                 if (PageCount >= 2)
706                 {
707                     navigationPages[PageCount - 2].SetVisible(true);
708                     NotifyAccessibilityStatesChangeOfPages(page, navigationPages[PageCount - 2]);
709                 }
710                 else if (PageCount == 1)
711                 {
712                     NotifyAccessibilityStatesChangeOfPages(page, null);
713                 }
714             }
715             page.Navigator = null;
716             navigationPages.Remove(page);
717             base.Remove(page);
718         }
719
720         /// <summary>
721         /// Removes a page at the specified index of Navigator.
722         /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
723         /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
724         /// To find the current index of a page in Navigator, please use IndexOf(page).
725         /// </summary>
726         /// <param name="index">The index of a page in Navigator where the page will be removed.</param>
727         /// <exception cref="ArgumentOutOfRangeException">Thrown when the index is less than 0, or greater than or equal to the number of pages.</exception>
728         /// <since_tizen> 9 </since_tizen>
729         public void RemoveAt(int index)
730         {
731             if ((index < 0) || (index >= navigationPages.Count))
732             {
733                 throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than the number of pages.");
734             }
735
736             Remove(navigationPages[index]);
737         }
738
739         /// <summary>
740         /// Returns the page at the top of Navigator.
741         /// </summary>
742         /// <returns>The page at the top of Navigator.</returns>
743         /// <since_tizen> 9 </since_tizen>
744         public Page Peek()
745         {
746             if (navigationPages.Count == 0) return null;
747
748             return navigationPages[navigationPages.Count - 1];
749         }
750
751         /// <summary>
752         /// Gets or sets if Navigator pops the peek page when back button or back key is pressed and released.
753         /// </summary>
754         [EditorBrowsable(EditorBrowsableState.Never)]
755         public bool EnableBackNavigation
756         {
757             get
758             {
759                 return (bool)GetValue(EnableBackNavigationProperty);
760             }
761             set
762             {
763                 SetValue(EnableBackNavigationProperty, value);
764                 NotifyPropertyChanged();
765             }
766         }
767
768         private bool InternalEnableBackNavigation
769         {
770             set
771             {
772                 enableBackNavigation = value;
773             }
774             get
775             {
776                 return enableBackNavigation;
777             }
778         }
779
780         /// <summary>
781         /// Disposes Navigator and all children on it.
782         /// </summary>
783         /// <param name="type">Dispose type.</param>
784         [EditorBrowsable(EditorBrowsableState.Never)]
785         protected override void Dispose(DisposeTypes type)
786         {
787             if (disposed)
788             {
789                 return;
790             }
791
792             if (type == DisposeTypes.Explicit)
793             {
794                 foreach (Page page in navigationPages)
795                 {
796                     Utility.Dispose(page);
797                 }
798                 navigationPages.Clear();
799
800                 Window window;
801
802                 if (navigatorWindow.TryGetValue(this, out window) == true)
803                 {
804                     navigatorWindow.Remove(this);
805                     windowNavigator.Remove(window);
806                 }
807
808                 AddedToWindow -= OnAddedToWindow;
809                 RemovedFromWindow -= OnRemovedFromWindow;
810             }
811
812             base.Dispose(type);
813         }
814
815         /// <summary>
816         /// Returns the default navigator of the given window.
817         /// </summary>
818         /// <returns>The default navigator of the given window.</returns>
819         /// <exception cref="ArgumentNullException">Thrown when the argument window is null.</exception>
820         /// <since_tizen> 9 </since_tizen>
821         public static Navigator GetDefaultNavigator(Window window)
822         {
823             if (window == null)
824             {
825                 throw new ArgumentNullException(nameof(window), "window should not be null.");
826             }
827
828             if (windowNavigator.ContainsKey(window) == true)
829             {
830                 return windowNavigator[window];
831             }
832
833             var defaultNavigator = new Navigator();
834             defaultNavigator.WidthResizePolicy = ResizePolicyType.FillToParent;
835             defaultNavigator.HeightResizePolicy = ResizePolicyType.FillToParent;
836             window.Add(defaultNavigator);
837             windowNavigator.Add(window, defaultNavigator);
838             navigatorWindow.Add(defaultNavigator, window);
839
840             return defaultNavigator;
841         }
842
843         /// <summary>
844         /// Sets the default navigator of the given window.
845         /// SetDefaultNavigator does not remove the previous default navigator from the window.
846         /// Therefore, if a user does not want to show the previous default navigator, then it should be removed from the window manually.
847         /// </summary>
848         /// <exception cref="ArgumentNullException">Thrown when the argument window is null.</exception>
849         /// <exception cref="ArgumentNullException">Thrown when the argument newNavigator is null.</exception>
850         [EditorBrowsable(EditorBrowsableState.Never)]
851         public static void SetDefaultNavigator(Window window, Navigator newNavigator)
852         {
853             if (window == null)
854             {
855                 throw new ArgumentNullException(nameof(window), "window should not be null.");
856             }
857
858             if (newNavigator == null)
859             {
860                 throw new ArgumentNullException(nameof(newNavigator), "newNavigator should not be null.");
861             }
862
863             if (windowNavigator.ContainsKey(window) == true)
864             {
865                 var oldNavigator = windowNavigator[window];
866                 if (oldNavigator == newNavigator)
867                 {
868                     return;
869                 }
870
871                 navigatorWindow.Remove(oldNavigator);
872                 windowNavigator.Remove(window);
873             }
874
875             window.Add(newNavigator);
876             windowNavigator.Add(window, newNavigator);
877             navigatorWindow.Add(newNavigator, window);
878         }
879
880         /// <summary>
881         /// Called when the back navigation is started.
882         /// </summary>
883         /// <param name="eventArgs">The back navigation information.</param>
884         [EditorBrowsable(EditorBrowsableState.Never)]
885         protected virtual void OnBackNavigation(BackNavigationEventArgs eventArgs)
886         {
887             if (PageCount >= 1)
888             {
889                 if (Peek().EnableBackNavigation)
890                 {
891                     Peek().NavigateBack();
892                 }
893             }
894         }
895
896         /// <summary>
897         /// Called when the back navigation is required outside Navigator.
898         /// </summary>
899         internal void NavigateBack()
900         {
901             OnBackNavigation(new BackNavigationEventArgs());
902         }
903
904         /// <summary>
905         /// Create Transitions between currentTopPage and newTopPage
906         /// </summary>
907         /// <param name="currentTopPage">The top page of Navigator.</param>
908         /// <param name="newTopPage">The new top page after transition.</param>
909         /// <param name="pushTransition">True if this transition is for push new page</param>
910         private TransitionSet CreateTransitions(Page currentTopPage, Page newTopPage, bool pushTransition)
911         {
912             currentTopPage.SetVisible(true);
913             // Set Content visible because it was hidden by HideContentOfPage.
914             (currentTopPage as ContentPage)?.Content?.SetVisible(true);
915
916             newTopPage.SetVisible(true);
917             // Set Content visible because it was hidden by HideContentOfPage.
918             (newTopPage as ContentPage)?.Content?.SetVisible(true);
919
920             List<View> taggedViewsInNewTopPage = new List<View>();
921             RetrieveTaggedViews(taggedViewsInNewTopPage, newTopPage, true);
922             List<View> taggedViewsInCurrentTopPage = new List<View>();
923             RetrieveTaggedViews(taggedViewsInCurrentTopPage, currentTopPage, true);
924
925             List<KeyValuePair<View, View>> sameTaggedViewPair = new List<KeyValuePair<View, View>>();
926             foreach (View currentTopPageView in taggedViewsInCurrentTopPage)
927             {
928                 bool findPair = false;
929                 foreach (View newTopPageView in taggedViewsInNewTopPage)
930                 {
931                     if ((currentTopPageView.TransitionOptions != null) && (newTopPageView.TransitionOptions != null) &&
932                         currentTopPageView.TransitionOptions?.TransitionTag == newTopPageView.TransitionOptions?.TransitionTag)
933                     {
934                         sameTaggedViewPair.Add(new KeyValuePair<View, View>(currentTopPageView, newTopPageView));
935                         findPair = true;
936                         break;
937                     }
938                 }
939                 if (findPair)
940                 {
941                     taggedViewsInNewTopPage.Remove(sameTaggedViewPair[sameTaggedViewPair.Count - 1].Value);
942                 }
943             }
944             foreach (KeyValuePair<View, View> pair in sameTaggedViewPair)
945             {
946                 taggedViewsInCurrentTopPage.Remove(pair.Key);
947             }
948
949             TransitionSet newTransitionSet = new TransitionSet();
950             foreach (KeyValuePair<View, View> pair in sameTaggedViewPair)
951             {
952                 TransitionItem pairTransition = transition.CreateTransition(pair.Key, pair.Value, pushTransition);
953                 if (pair.Value.TransitionOptions?.TransitionWithChild ?? false)
954                 {
955                     pairTransition.TransitionWithChild = true;
956                 }
957                 newTransitionSet.AddTransition(pairTransition);
958             }
959
960             newTransitionSet.Finished += (object sender, EventArgs e) =>
961             {
962                 if (newTopPage.Layout != null)
963                 {
964                     newTopPage.Layout.RequestLayout();
965                 }
966                 if (currentTopPage.Layout != null)
967                 {
968                     currentTopPage.Layout.RequestLayout();
969                 }
970                 transitionFinished = true;
971                 InvokeTransitionFinished();
972                 transitionSet.Dispose();
973             };
974
975             if (!pushTransition || newTopPage is DialogPage == false)
976             {
977                 View transitionView = (currentTopPage is ContentPage) ? (currentTopPage as ContentPage).Content : (currentTopPage as DialogPage).Content;
978                 if (currentTopPage.DisappearingTransition != null && transitionView != null)
979                 {
980                     TransitionItemBase disappearingTransition = currentTopPage.DisappearingTransition.CreateTransition(transitionView, false);
981                     disappearingTransition.TransitionWithChild = true;
982                     newTransitionSet.AddTransition(disappearingTransition);
983                 }
984                 else
985                 {
986                     currentTopPage.SetVisible(false);
987                 }
988             }
989             if (pushTransition || currentTopPage is DialogPage == false)
990             {
991                 View transitionView = (newTopPage is ContentPage) ? (newTopPage as ContentPage).Content : (newTopPage as DialogPage).Content;
992                 if (newTopPage.AppearingTransition != null && transitionView != null)
993                 {
994                     TransitionItemBase appearingTransition = newTopPage.AppearingTransition.CreateTransition(transitionView, true);
995                     appearingTransition.TransitionWithChild = true;
996                     newTransitionSet.AddTransition(appearingTransition);
997                 }
998             }
999
1000             newTransitionSet.Play();
1001
1002             return newTransitionSet;
1003         }
1004
1005         /// <summary>
1006         /// Retrieve Tagged Views in the view tree.
1007         /// </summary>
1008         /// <param name="taggedViews">Returned tagged view list..</param>
1009         /// <param name="view">Root View to get tagged child View.</param>
1010         /// <param name="isRoot">Flag to check current View is page or not</param>
1011         private void RetrieveTaggedViews(List<View> taggedViews, View view, bool isRoot)
1012         {
1013             if (!isRoot && view.TransitionOptions != null)
1014             {
1015                 if (!string.IsNullOrEmpty(view.TransitionOptions?.TransitionTag))
1016                 {
1017                     taggedViews.Add((view as View));
1018                     if (view.TransitionOptions.TransitionWithChild)
1019                     {
1020                         return;
1021                     }
1022                 }
1023
1024             }
1025
1026             foreach (View child in view.Children)
1027             {
1028                 RetrieveTaggedViews(taggedViews, child, false);
1029             }
1030         }
1031
1032         /// <summary>
1033         /// Notify accessibility states change of pages.
1034         /// </summary>
1035         /// <param name="disappearedPage">Disappeared page</param>
1036         /// <param name="appearedPage">Appeared page</param>
1037         private void NotifyAccessibilityStatesChangeOfPages(Page disappearedPage, Page appearedPage)
1038         {
1039             if (disappearedPage != null)
1040             {
1041                 disappearedPage.UnregisterDefaultLabel();
1042                 //We can call disappearedPage.NotifyAccessibilityStatesChange
1043                 //To reduce accessibility events, we are using currently highlighted view instead
1044                 View curHighlightedView = Accessibility.Accessibility.GetCurrentlyHighlightedView();
1045                 if (curHighlightedView != null)
1046                 {
1047                     curHighlightedView.NotifyAccessibilityStatesChange(new AccessibilityStates(AccessibilityState.Visible, AccessibilityState.Showing), AccessibilityStatesNotifyMode.Single);
1048                 }
1049             }
1050
1051             if (appearedPage != null)
1052             {
1053                 appearedPage.RegisterDefaultLabel();
1054                 appearedPage.NotifyAccessibilityStatesChange(new AccessibilityStates(AccessibilityState.Visible, AccessibilityState.Showing), AccessibilityStatesNotifyMode.Single);
1055             }
1056         }
1057
1058         internal void InvokeTransitionFinished()
1059         {
1060             TransitionFinished?.Invoke(this, new EventArgs());
1061         }
1062
1063         //TODO: The following transition codes will be replaced with view transition.
1064         private void InitializeAnimation()
1065         {
1066             if (curAnimation != null)
1067             {
1068                 curAnimation.Stop();
1069                 curAnimation.Clear();
1070                 curAnimation = null;
1071             }
1072
1073             if (newAnimation != null)
1074             {
1075                 newAnimation.Stop();
1076                 newAnimation.Clear();
1077                 newAnimation = null;
1078             }
1079         }
1080
1081         // Show and Register Content of Page to Accessibility bridge
1082         private void ShowContentOfPage(Page page)
1083         {
1084             View content = (page is DialogPage) ? (page as DialogPage)?.Content : (page as ContentPage)?.Content;
1085             if (content != null)
1086             {
1087                 content.Show(); // Calls RegisterDefaultLabel()
1088             }
1089         }
1090
1091         // Hide and Remove Content of Page from Accessibility bridge
1092         private void HideContentOfPage(Page page)
1093         {
1094             View content = (page is DialogPage) ? (page as DialogPage)?.Content : (page as ContentPage)?.Content;
1095             if (content != null)
1096             {
1097                 content.Hide(); // Calls UnregisterDefaultLabel()
1098             }
1099         }
1100     }
1101
1102     /// <summary>
1103     /// BackNavigationEventArgs is a class to record back navigation event arguments which will sent to user.
1104     /// </summary>
1105     [EditorBrowsable(EditorBrowsableState.Never)]
1106     public class BackNavigationEventArgs : EventArgs
1107     {
1108     }
1109 }