b0ccc7a2f2e9468debe62bcdc16032ca0bf3dd2d
[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         private const int DefaultTransitionDuration = 300;
96
97         //This will be replaced with view transition class instance.
98         private Animation curAnimation = null;
99
100         //This will be replaced with view transition class instance.
101         private Animation newAnimation = null;
102
103         private TransitionSet transitionSet = null;
104
105         private Transition transition = new Transition()
106         {
107             TimePeriod = new TimePeriod(DefaultTransitionDuration),
108             AlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.Default),
109         };
110
111         private bool transitionFinished = true;
112
113         //TODO: Needs to consider how to remove disposed window from dictionary.
114         //Two dictionaries are required to remove disposed navigator from dictionary.
115         private static Dictionary<Window, Navigator> windowNavigator = new Dictionary<Window, Navigator>();
116         private static Dictionary<Navigator, Window> navigatorWindow = new Dictionary<Navigator, Window>();
117
118         private List<Page> navigationPages = new List<Page>();
119
120         private void Initialize()
121         {
122             Layout = new AbsoluteLayout();
123         }
124
125         /// <summary>
126         /// Creates a new instance of a Navigator.
127         /// </summary>
128         /// <since_tizen> 9 </since_tizen>
129         public Navigator() : base()
130         {
131             Initialize();
132         }
133
134         /// <summary>
135         /// Creates a new instance of Navigator with style.
136         /// </summary>
137         /// <param name="style">Creates Navigator by special style defined in UX.</param>
138         [EditorBrowsable(EditorBrowsableState.Never)]
139         public Navigator(string style) : base(style)
140         {
141             Initialize();
142         }
143
144         /// <summary>
145         /// Creates a new instance of a Navigator with style.
146         /// </summary>
147         /// <param name="style">A style applied to the newly created Navigator.</param>
148         [EditorBrowsable(EditorBrowsableState.Never)]
149         public Navigator(ControlStyle style) : base(style)
150         {
151             Initialize();
152         }
153
154         /// <inheritdoc/>
155         [EditorBrowsable(EditorBrowsableState.Never)]
156         public override void OnInitialize()
157         {
158             base.OnInitialize();
159
160             AccessibilityRole = Role.PageTabList;
161         }
162
163         /// <summary>
164         /// An event fired when Transition has been finished.
165         /// </summary>
166         /// <since_tizen> 9 </since_tizen>
167         public event EventHandler<EventArgs> TransitionFinished;
168
169         /// <summary>
170         /// An event fired when Pop of a page has been finished.
171         /// </summary>
172         /// <remarks>
173         /// When you free resources in the Popped event handler, please make sure if the popped page is the page you find.
174         /// </remarks>
175         /// <since_tizen> 9 </since_tizen>
176         public event EventHandler<PoppedEventArgs> Popped;
177
178         /// <summary>
179         /// Returns the count of pages in Navigator.
180         /// </summary>
181         /// <since_tizen> 9 </since_tizen>
182         public int PageCount => navigationPages.Count;
183
184         /// <summary>
185         /// Transition properties for the transition of View pair having same transition tag.
186         /// </summary>
187         /// <since_tizen> 9 </since_tizen>
188         public Transition Transition
189         {
190             get
191             {
192                 return GetValue(TransitionProperty) as Transition;
193             }
194             set
195             {
196                 SetValue(TransitionProperty, value);
197                 NotifyPropertyChanged();
198             }
199         }
200         private Transition InternalTransition
201         {
202             set
203             {
204                 transition = value;
205             }
206             get
207             {
208                 return transition;
209             }
210         }
211
212         /// <summary>
213         /// Pushes a page to Navigator.
214         /// If the page is already in Navigator, then it is not pushed.
215         /// </summary>
216         /// <param name="page">The page to push to Navigator.</param>
217         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
218         /// <since_tizen> 9 </since_tizen>
219         public void PushWithTransition(Page page)
220         {
221             if (!transitionFinished)
222             {
223                 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
224                 return;
225             }
226
227             if (page == null)
228             {
229                 throw new ArgumentNullException(nameof(page), "page should not be null.");
230             }
231
232             //Duplicate page is not pushed.
233             if (navigationPages.Contains(page)) return;
234
235             var topPage = Peek();
236
237             if (!topPage)
238             {
239                 Insert(0, page);
240                 return;
241             }
242
243             navigationPages.Add(page);
244             Add(page);
245             page.Navigator = this;
246
247             //Invoke Page events
248             page.InvokeAppearing();
249             topPage.InvokeDisappearing();
250             topPage.SaveKeyFocus();
251
252             transitionSet = CreateTransitions(topPage, page, true);
253             transitionSet.Finished += (object sender, EventArgs e) =>
254             {
255                 if (page is DialogPage == false)
256                 {
257                     topPage.SetVisible(false);
258                 }
259
260                 // Need to update Content of the new page
261                 ShowContentOfPage(page);
262
263                 //Invoke Page events
264                 page.InvokeAppeared();
265                 page.RestoreKeyFocus();
266                 
267                 topPage.InvokeDisappeared();
268                 NotifyAccessibilityStatesChangeOfPages(topPage, page);
269             };
270             transitionFinished = false;
271         }
272
273         /// <summary>
274         /// Pops the top page from Navigator.
275         /// </summary>
276         /// <returns>The popped page.</returns>
277         /// <exception cref="InvalidOperationException">Thrown when there is no page in Navigator.</exception>
278         /// <since_tizen> 9 </since_tizen>
279         public Page PopWithTransition()
280         {
281             if (!transitionFinished)
282             {
283                 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
284                 return null;
285             }
286
287             if (navigationPages.Count == 0)
288             {
289                 throw new InvalidOperationException("There is no page in Navigator.");
290             }
291
292             var topPage = Peek();
293
294             if (navigationPages.Count == 1)
295             {
296                 Remove(topPage);
297
298                 //Invoke Popped event
299                 Popped?.Invoke(this, new PoppedEventArgs() { Page = topPage });
300
301                 return topPage;
302             }
303             var newTopPage = navigationPages[navigationPages.Count - 2];
304
305             //Invoke Page events
306             newTopPage.InvokeAppearing();
307             topPage.InvokeDisappearing();
308             topPage.SaveKeyFocus();
309
310             transitionSet = CreateTransitions(topPage, newTopPage, false);
311             transitionSet.Finished += (object sender, EventArgs e) =>
312             {
313                 Remove(topPage);
314                 topPage.SetVisible(true);
315
316                 // Need to update Content of the new page
317                 ShowContentOfPage(newTopPage);
318
319                 //Invoke Page events
320                 newTopPage.InvokeAppeared();
321                 newTopPage.RestoreKeyFocus();
322
323                 topPage.InvokeDisappeared();
324
325                 //Invoke Popped event
326                 Popped?.Invoke(this, new PoppedEventArgs() { Page = topPage });
327             };
328             transitionFinished = false;
329
330             return topPage;
331         }
332
333         /// <summary>
334         /// Pushes a page to Navigator.
335         /// If the page is already in Navigator, then it is not pushed.
336         /// </summary>
337         /// <param name="page">The page to push to Navigator.</param>
338         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
339         /// <since_tizen> 9 </since_tizen>
340         public void Push(Page page)
341         {
342             if (!transitionFinished)
343             {
344                 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
345                 return;
346             }
347
348             if (page == null)
349             {
350                 throw new ArgumentNullException(nameof(page), "page should not be null.");
351             }
352
353             //Duplicate page is not pushed.
354             if (navigationPages.Contains(page)) return;
355
356             var curTop = Peek();
357
358             if (!curTop)
359             {
360                 Insert(0, page);
361                 return;
362             }
363
364             navigationPages.Add(page);
365             Add(page);
366             page.Navigator = this;
367
368             //Invoke Page events
369             page.InvokeAppearing();
370             curTop.InvokeDisappearing();
371
372             curTop.SaveKeyFocus();
373
374             //TODO: The following transition codes will be replaced with view transition.
375             InitializeAnimation();
376
377             if (page is DialogPage == false)
378             {
379                 curAnimation = new Animation(DefaultTransitionDuration);
380                 curAnimation.AnimateTo(curTop, "PositionX", 0.0f, 0, DefaultTransitionDuration);
381                 curAnimation.EndAction = Animation.EndActions.StopFinal;
382                 curAnimation.Finished += (object sender, EventArgs args) =>
383                 {
384                     curTop.SetVisible(false);
385
386                     //Invoke Page events
387                     curTop.InvokeDisappeared();
388                 };
389                 curAnimation.Play();
390
391                 page.PositionX = SizeWidth;
392                 page.SetVisible(true);
393                 // Set Content visible because it was hidden by HideContentOfPage.
394                 (page as ContentPage)?.Content?.SetVisible(true);
395
396                 newAnimation = new Animation(DefaultTransitionDuration);
397                 newAnimation.AnimateTo(page, "PositionX", 0.0f, 0, DefaultTransitionDuration);
398                 newAnimation.EndAction = Animation.EndActions.StopFinal;
399                 newAnimation.Finished += (object sender, EventArgs e) =>
400                 {
401                     // Need to update Content of the new page
402                     ShowContentOfPage(page);
403
404                     //Invoke Page events
405                     page.InvokeAppeared();
406                     NotifyAccessibilityStatesChangeOfPages(curTop, page);
407
408                     page.RestoreKeyFocus();
409                 };
410                 newAnimation.Play();
411             }
412             else
413             {
414                 ShowContentOfPage(page);
415                 page.RestoreKeyFocus();
416             }
417         }
418
419         /// <summary>
420         /// Pops the top page from Navigator.
421         /// </summary>
422         /// <returns>The popped page.</returns>
423         /// <exception cref="InvalidOperationException">Thrown when there is no page in Navigator.</exception>
424         /// <since_tizen> 9 </since_tizen>
425         public Page Pop()
426         {
427             if (!transitionFinished)
428             {
429                 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
430                 return null;
431             }
432
433             if (navigationPages.Count == 0)
434             {
435                 throw new InvalidOperationException("There is no page in Navigator.");
436             }
437
438             var curTop = Peek();
439
440             if (navigationPages.Count == 1)
441             {
442                 Remove(curTop);
443
444                 //Invoke Popped event
445                 Popped?.Invoke(this, new PoppedEventArgs() { Page = curTop });
446
447                 return curTop;
448             }
449
450             var newTop = navigationPages[navigationPages.Count - 2];
451
452             //Invoke Page events
453             newTop.InvokeAppearing();
454             curTop.InvokeDisappearing();
455             curTop.SaveKeyFocus();
456
457             //TODO: The following transition codes will be replaced with view transition.
458             InitializeAnimation();
459
460             if (curTop is DialogPage == false)
461             {
462                 curAnimation = new Animation(DefaultTransitionDuration);
463                 curAnimation.AnimateTo(curTop, "PositionX", SizeWidth, 0, DefaultTransitionDuration);
464                 curAnimation.EndAction = Animation.EndActions.StopFinal;
465                 curAnimation.Finished += (object sender, EventArgs e) =>
466                 {
467                     //Removes the current top page after transition is finished.
468                     Remove(curTop);
469                     curTop.PositionX = 0.0f;
470
471                     //Invoke Page events
472                     curTop.InvokeDisappeared();
473
474                     //Invoke Popped event
475                     Popped?.Invoke(this, new PoppedEventArgs() { Page = curTop });
476                 };
477                 curAnimation.Play();
478
479                 newTop.SetVisible(true);
480                 // Set Content visible because it was hidden by HideContentOfPage.
481                 (newTop as ContentPage)?.Content?.SetVisible(true);
482
483                 newAnimation = new Animation(DefaultTransitionDuration);
484                 newAnimation.AnimateTo(newTop, "PositionX", 0.0f, 0, DefaultTransitionDuration);
485                 newAnimation.EndAction = Animation.EndActions.StopFinal;
486                 newAnimation.Finished += (object sender, EventArgs e) =>
487                 {
488                     // Need to update Content of the new page
489                     ShowContentOfPage(newTop);
490
491                     //Invoke Page events
492                     newTop.InvokeAppeared();
493
494                     newTop.RestoreKeyFocus();
495                 };
496                 newAnimation.Play();
497             }
498             else
499             {
500                 Remove(curTop);
501                 newTop.RestoreKeyFocus();
502             }
503
504             return curTop;
505         }
506
507         /// <summary>
508         /// Returns the page of the given index in Navigator.
509         /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
510         /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
511         /// </summary>
512         /// <param name="index">The index of a page in Navigator.</param>
513         /// <returns>The page of the given index in Navigator.</returns>
514         /// <exception cref="ArgumentOutOfRangeException">Thrown when the argument index is less than 0, or greater than the number of pages.</exception>
515         public Page GetPage(int index)
516         {
517             if ((index < 0) || (index > navigationPages.Count))
518             {
519                 throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than or equal to the number of pages.");
520             }
521
522             return navigationPages[index];
523         }
524
525         /// <summary>
526         /// Returns the current index of the given page in Navigator.
527         /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
528         /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
529         /// </summary>
530         /// <param name="page">The page in Navigator.</param>
531         /// <returns>The index of the given page in Navigator. If the given page is not in the Navigator, then -1 is returned.</returns>
532         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
533         /// <since_tizen> 9 </since_tizen>
534         public int IndexOf(Page page)
535         {
536             if (page == null)
537             {
538                 throw new ArgumentNullException(nameof(page), "page should not be null.");
539             }
540
541             for (int i = 0; i < navigationPages.Count; i++)
542             {
543                 if (navigationPages[i] == page)
544                 {
545                     return i;
546                 }
547             }
548
549             return -1;
550         }
551
552         /// <summary>
553         /// Inserts a page at the specified index of Navigator.
554         /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
555         /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
556         /// To find the current index of a page in Navigator, please use IndexOf(page).
557         /// If the page is already in Navigator, then it is not inserted.
558         /// </summary>
559         /// <param name="index">The index of a page in Navigator where the page will be inserted.</param>
560         /// <param name="page">The page to insert to Navigator.</param>
561         /// <exception cref="ArgumentOutOfRangeException">Thrown when the argument index is less than 0, or greater than the number of pages.</exception>
562         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
563         /// <since_tizen> 9 </since_tizen>
564         public void Insert(int index, Page page)
565         {
566             if ((index < 0) || (index > navigationPages.Count))
567             {
568                 throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than or equal to the number of pages.");
569             }
570
571             if (page == null)
572             {
573                 throw new ArgumentNullException(nameof(page), "page should not be null.");
574             }
575
576             //Duplicate page is not pushed.
577             if (navigationPages.Contains(page)) return;
578
579             //TODO: The following transition codes will be replaced with view transition.
580             InitializeAnimation();
581
582             ShowContentOfPage(page);
583
584             if (index == PageCount)
585             {
586                 page.SetVisible(true);
587             }
588             else
589             {
590                 page.SetVisible(false);
591             }
592
593             navigationPages.Insert(index, page);
594             Add(page);
595             page.Navigator = this;
596             if (index == PageCount - 1)
597             {
598                 if (PageCount > 1)
599                 {
600                     NotifyAccessibilityStatesChangeOfPages(navigationPages[PageCount - 2], page);
601                 }
602                 else
603                 {
604                     NotifyAccessibilityStatesChangeOfPages(null, page);
605                 }
606             }
607         }
608
609         /// <summary>
610         /// Inserts a page to Navigator before an existing page.
611         /// If the page is already in Navigator, then it is not inserted.
612         /// </summary>
613         /// <param name="before">The existing page, before which a page will be inserted.</param>
614         /// <param name="page">The page to insert to Navigator.</param>
615         /// <exception cref="ArgumentNullException">Thrown when the argument before is null.</exception>
616         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
617         /// <exception cref="ArgumentException">Thrown when the argument before does not exist in Navigator.</exception>
618         /// <since_tizen> 9 </since_tizen>
619         public void InsertBefore(Page before, Page page)
620         {
621             if (before == null)
622             {
623                 throw new ArgumentNullException(nameof(before), "before should not be null.");
624             }
625
626             if (page == null)
627             {
628                 throw new ArgumentNullException(nameof(page), "page should not be null.");
629             }
630
631             //Find the index of before page.
632             int beforeIndex = navigationPages.FindIndex(x => x == before);
633
634             //before does not exist in Navigator.
635             if (beforeIndex == -1)
636             {
637                 throw new ArgumentException("before does not exist in Navigator.", nameof(before));
638             }
639
640             Insert(beforeIndex, page);
641         }
642
643         /// <summary>
644         /// Removes a page from Navigator.
645         /// </summary>
646         /// <param name="page">The page to remove from Navigator.</param>
647         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
648         /// <since_tizen> 9 </since_tizen>
649         public void Remove(Page page)
650         {
651             if (page == null)
652             {
653                 throw new ArgumentNullException(nameof(page), "page should not be null.");
654             }
655
656             //TODO: The following transition codes will be replaced with view transition.
657             InitializeAnimation();
658
659             HideContentOfPage(page);
660
661             if (page == Peek())
662             {
663                 if (PageCount >= 2)
664                 {
665                     navigationPages[PageCount - 2].SetVisible(true);
666                     NotifyAccessibilityStatesChangeOfPages(page, navigationPages[PageCount - 2]);
667                 }
668                 else if (PageCount == 1)
669                 {
670                     NotifyAccessibilityStatesChangeOfPages(page, null);
671                 }
672             }
673             page.Navigator = null;
674             navigationPages.Remove(page);
675             base.Remove(page);
676         }
677
678         /// <summary>
679         /// Removes a page at the specified index of Navigator.
680         /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
681         /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
682         /// To find the current index of a page in Navigator, please use IndexOf(page).
683         /// </summary>
684         /// <param name="index">The index of a page in Navigator where the page will be removed.</param>
685         /// <exception cref="ArgumentOutOfRangeException">Thrown when the index is less than 0, or greater than or equal to the number of pages.</exception>
686         /// <since_tizen> 9 </since_tizen>
687         public void RemoveAt(int index)
688         {
689             if ((index < 0) || (index >= navigationPages.Count))
690             {
691                 throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than the number of pages.");
692             }
693
694             Remove(navigationPages[index]);
695         }
696
697         /// <summary>
698         /// Returns the page at the top of Navigator.
699         /// </summary>
700         /// <returns>The page at the top of Navigator.</returns>
701         /// <since_tizen> 9 </since_tizen>
702         public Page Peek()
703         {
704             if (navigationPages.Count == 0) return null;
705
706             return navigationPages[navigationPages.Count - 1];
707         }
708
709         /// <summary>
710         /// Disposes Navigator and all children on it.
711         /// </summary>
712         /// <param name="type">Dispose type.</param>
713         [EditorBrowsable(EditorBrowsableState.Never)]
714         protected override void Dispose(DisposeTypes type)
715         {
716             if (disposed)
717             {
718                 return;
719             }
720
721             if (type == DisposeTypes.Explicit)
722             {
723                 foreach (Page page in navigationPages)
724                 {
725                     Utility.Dispose(page);
726                 }
727                 navigationPages.Clear();
728
729                 Window window;
730
731                 if (navigatorWindow.TryGetValue(this, out window) == true)
732                 {
733                     navigatorWindow.Remove(this);
734                     windowNavigator.Remove(window);
735                 }
736             }
737
738             base.Dispose(type);
739         }
740
741         /// <summary>
742         /// Returns the default navigator of the given window.
743         /// </summary>
744         /// <returns>The default navigator of the given window.</returns>
745         /// <exception cref="ArgumentNullException">Thrown when the argument window is null.</exception>
746         /// <since_tizen> 9 </since_tizen>
747         public static Navigator GetDefaultNavigator(Window window)
748         {
749             if (window == null)
750             {
751                 throw new ArgumentNullException(nameof(window), "window should not be null.");
752             }
753
754             if (windowNavigator.ContainsKey(window) == true)
755             {
756                 return windowNavigator[window];
757             }
758
759             var defaultNavigator = new Navigator();
760             defaultNavigator.WidthResizePolicy = ResizePolicyType.FillToParent;
761             defaultNavigator.HeightResizePolicy = ResizePolicyType.FillToParent;
762             window.Add(defaultNavigator);
763             windowNavigator.Add(window, defaultNavigator);
764             navigatorWindow.Add(defaultNavigator, window);
765
766             return defaultNavigator;
767         }
768
769         /// <summary>
770         /// Create Transitions between currentTopPage and newTopPage
771         /// </summary>
772         /// <param name="currentTopPage">The top page of Navigator.</param>
773         /// <param name="newTopPage">The new top page after transition.</param>
774         /// <param name="pushTransition">True if this transition is for push new page</param>
775         private TransitionSet CreateTransitions(Page currentTopPage, Page newTopPage, bool pushTransition)
776         {
777             currentTopPage.SetVisible(true);
778             // Set Content visible because it was hidden by HideContentOfPage.
779             (currentTopPage as ContentPage)?.Content?.SetVisible(true);
780
781             newTopPage.SetVisible(true);
782             // Set Content visible because it was hidden by HideContentOfPage.
783             (newTopPage as ContentPage)?.Content?.SetVisible(true);
784
785             List<View> taggedViewsInNewTopPage = new List<View>();
786             RetrieveTaggedViews(taggedViewsInNewTopPage, newTopPage, true);
787             List<View> taggedViewsInCurrentTopPage = new List<View>();
788             RetrieveTaggedViews(taggedViewsInCurrentTopPage, currentTopPage, true);
789
790             List<KeyValuePair<View, View>> sameTaggedViewPair = new List<KeyValuePair<View, View>>();
791             foreach (View currentTopPageView in taggedViewsInCurrentTopPage)
792             {
793                 bool findPair = false;
794                 foreach (View newTopPageView in taggedViewsInNewTopPage)
795                 {
796                     if ((currentTopPageView.TransitionOptions != null) && (newTopPageView.TransitionOptions != null) &&
797                         currentTopPageView.TransitionOptions?.TransitionTag == newTopPageView.TransitionOptions?.TransitionTag)
798                     {
799                         sameTaggedViewPair.Add(new KeyValuePair<View, View>(currentTopPageView, newTopPageView));
800                         findPair = true;
801                         break;
802                     }
803                 }
804                 if (findPair)
805                 {
806                     taggedViewsInNewTopPage.Remove(sameTaggedViewPair[sameTaggedViewPair.Count - 1].Value);
807                 }
808             }
809             foreach (KeyValuePair<View, View> pair in sameTaggedViewPair)
810             {
811                 taggedViewsInCurrentTopPage.Remove(pair.Key);
812             }
813
814             TransitionSet newTransitionSet = new TransitionSet();
815             foreach (KeyValuePair<View, View> pair in sameTaggedViewPair)
816             {
817                 TransitionItem pairTransition = transition.CreateTransition(pair.Key, pair.Value, pushTransition);
818                 if (pair.Value.TransitionOptions?.TransitionWithChild ?? false)
819                 {
820                     pairTransition.TransitionWithChild = true;
821                 }
822                 newTransitionSet.AddTransition(pairTransition);
823             }
824
825             newTransitionSet.Finished += (object sender, EventArgs e) =>
826             {
827                 if (newTopPage.Layout != null)
828                 {
829                     newTopPage.Layout.RequestLayout();
830                 }
831                 if (currentTopPage.Layout != null)
832                 {
833                     currentTopPage.Layout.RequestLayout();
834                 }
835                 transitionFinished = true;
836                 InvokeTransitionFinished();
837                 transitionSet.Dispose();
838             };
839
840             if (!pushTransition || newTopPage is DialogPage == false)
841             {
842                 View transitionView = (currentTopPage is ContentPage) ? (currentTopPage as ContentPage).Content : (currentTopPage as DialogPage).Content;
843                 if (currentTopPage.DisappearingTransition != null && transitionView != null)
844                 {
845                     TransitionItemBase disappearingTransition = currentTopPage.DisappearingTransition.CreateTransition(transitionView, false);
846                     disappearingTransition.TransitionWithChild = true;
847                     newTransitionSet.AddTransition(disappearingTransition);
848                 }
849                 else
850                 {
851                     currentTopPage.SetVisible(false);
852                 }
853             }
854             if (pushTransition || currentTopPage is DialogPage == false)
855             {
856                 View transitionView = (newTopPage is ContentPage) ? (newTopPage as ContentPage).Content : (newTopPage as DialogPage).Content;
857                 if (newTopPage.AppearingTransition != null && transitionView != null)
858                 {
859                     TransitionItemBase appearingTransition = newTopPage.AppearingTransition.CreateTransition(transitionView, true);
860                     appearingTransition.TransitionWithChild = true;
861                     newTransitionSet.AddTransition(appearingTransition);
862                 }
863             }
864
865             newTransitionSet.Play();
866
867             return newTransitionSet;
868         }
869
870         /// <summary>
871         /// Retrieve Tagged Views in the view tree.
872         /// </summary>
873         /// <param name="taggedViews">Returned tagged view list..</param>
874         /// <param name="view">Root View to get tagged child View.</param>
875         /// <param name="isRoot">Flag to check current View is page or not</param>
876         private void RetrieveTaggedViews(List<View> taggedViews, View view, bool isRoot)
877         {
878             if (!isRoot && view.TransitionOptions != null)
879             {
880                 if (!string.IsNullOrEmpty(view.TransitionOptions?.TransitionTag))
881                 {
882                     taggedViews.Add((view as View));
883                     if (view.TransitionOptions.TransitionWithChild)
884                     {
885                         return;
886                     }
887                 }
888
889             }
890
891             foreach (View child in view.Children)
892             {
893                 RetrieveTaggedViews(taggedViews, child, false);
894             }
895         }
896
897         /// <summary>
898         /// Notify accessibility states change of pages.
899         /// </summary>
900         /// <param name="disappearedPage">Disappeared page</param>
901         /// <param name="appearedPage">Appeared page</param>
902         private void NotifyAccessibilityStatesChangeOfPages(Page disappearedPage, Page appearedPage)
903         {
904             if (disappearedPage != null)
905             {
906                 disappearedPage.UnregisterDefaultLabel();
907                 //We can call disappearedPage.NotifyAccessibilityStatesChange
908                 //To reduce accessibility events, we are using currently highlighted view instead
909                 View curHighlightedView = Accessibility.Accessibility.GetCurrentlyHighlightedView();
910                 if (curHighlightedView != null)
911                 {
912                     curHighlightedView.NotifyAccessibilityStatesChange(new AccessibilityStates(AccessibilityState.Visible, AccessibilityState.Showing), AccessibilityStatesNotifyMode.Single);
913                 }
914             }
915
916             if (appearedPage != null)
917             {
918                 appearedPage.RegisterDefaultLabel();
919                 appearedPage.NotifyAccessibilityStatesChange(new AccessibilityStates(AccessibilityState.Visible, AccessibilityState.Showing), AccessibilityStatesNotifyMode.Single);
920             }
921         }
922
923         internal void InvokeTransitionFinished()
924         {
925             TransitionFinished?.Invoke(this, new EventArgs());
926         }
927
928         //TODO: The following transition codes will be replaced with view transition.
929         private void InitializeAnimation()
930         {
931             if (curAnimation != null)
932             {
933                 curAnimation.Stop();
934                 curAnimation.Clear();
935                 curAnimation = null;
936             }
937
938             if (newAnimation != null)
939             {
940                 newAnimation.Stop();
941                 newAnimation.Clear();
942                 newAnimation = null;
943             }
944         }
945
946         // Show and Register Content of Page to Accessibility bridge
947         private void ShowContentOfPage(Page page)
948         {
949             View content = (page is DialogPage) ? (page as DialogPage)?.Content : (page as ContentPage)?.Content;
950             if (content != null)
951             {
952                 content.Show(); // Calls RegisterDefaultLabel()
953             }
954         }
955
956         // Hide and Remove Content of Page from Accessibility bridge
957         private void HideContentOfPage(Page page)
958         {
959             View content = (page is DialogPage) ? (page as DialogPage)?.Content : (page as ContentPage)?.Content;
960             if (content != null)
961             {
962                 content.Hide(); // Calls UnregisterDefaultLabel()
963             }
964         }
965     }
966 }