[NUI] Make Navigator.Popped event public
[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
23 namespace Tizen.NUI.Components
24 {
25     /// <summary>
26     /// PoppedEventArgs is a class to record <see cref="Navigator.Popped"/> event arguments which will be sent to user.
27     /// </summary>
28     /// <since_tizen> 9 </since_tizen>
29     public class PoppedEventArgs : EventArgs
30     {
31         /// <summary>
32         /// Page popped by Navigator.
33         /// </summary>
34         /// <since_tizen> 9 </since_tizen>
35         public Page Page { get; internal set; }
36     }
37
38     /// <summary>
39     /// The Navigator is a class which navigates pages with stack methods such as Push and Pop.
40     /// </summary>
41     /// <remarks>
42     /// With Transition class, Navigator supports smooth transition of View pair between two Pages
43     /// by using <see cref="PushWithTransition(Page)"/> and <see cref="PopWithTransition()"/> methods.
44     /// If current top Page and next top Page have <see cref="View"/>s those have same TransitionTag,
45     /// Navigator creates smooth transition motion for them.
46     /// Navigator.Transition property can be used to set properties of the Transition such as TimePeriod and AlphaFunction.
47     /// When all transitions are finished, Navigator calls a callback methods those connected on the "TransitionFinished" event.
48     /// </remarks>
49     /// <example>
50     /// <code>
51     /// Navigator navigator = new Navigator()
52     /// {
53     ///     TimePeriod = new TimePeriod(500),
54     ///     AlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.EaseInOutSine)
55     /// };
56     ///
57     /// View view = new View()
58     /// {
59     ///     TransitionOptions = new TransitionOptions()
60     ///     {
61     ///         /* Set properties for the transition of this View */
62     ///     }
63     /// };
64     ///
65     /// ContentPage newPage = new ContentPage()
66     /// {
67     ///     Content = view,
68     /// };
69     ///
70     /// Navigator.PushWithTransition(newPage);
71     /// </code>
72     /// </example>
73     /// <since_tizen> 9 </since_tizen>
74     public class Navigator : Control
75     {
76         private const int DefaultTransitionDuration = 500;
77
78         //This will be replaced with view transition class instance.
79         private Animation curAnimation = null;
80
81         //This will be replaced with view transition class instance.
82         private Animation newAnimation = null;
83
84         private TransitionSet transitionSet = null;
85
86         private Transition transition = new Transition()
87         {
88             TimePeriod = new TimePeriod(DefaultTransitionDuration),
89             AlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.Default),
90         };
91
92         private bool transitionFinished = true;
93
94         //TODO: Needs to consider how to remove disposed window from dictionary.
95         //Two dictionaries are required to remove disposed navigator from dictionary.
96         private static Dictionary<Window, Navigator> windowNavigator = new Dictionary<Window, Navigator>();
97         private static Dictionary<Navigator, Window> navigatorWindow = new Dictionary<Navigator, Window>();
98
99         private List<Page> navigationPages = new List<Page>();
100
101         /// <summary>
102         /// Creates a new instance of a Navigator.
103         /// </summary>
104         /// <since_tizen> 9 </since_tizen>
105         public Navigator() : base()
106         {
107             Layout = new AbsoluteLayout();
108         }
109         
110         /// <summary>
111         /// An event fired when Transition has been finished.
112         /// </summary>
113         /// <since_tizen> 9 </since_tizen>
114         public event EventHandler<EventArgs> TransitionFinished;
115
116         /// <summary>
117         /// An event fired when Pop of a page has been finished.
118         /// </summary>
119         /// <remarks>
120         /// When you free resources in the Popped event handler, please make sure if the popped page is the page you find.
121         /// </remarks>
122         /// <since_tizen> 9 </since_tizen>
123         public event EventHandler<PoppedEventArgs> Popped;
124
125         /// <summary>
126         /// Returns the count of pages in Navigator.
127         /// </summary>
128         /// <since_tizen> 9 </since_tizen>
129         public int PageCount => navigationPages.Count;
130
131         /// <summary>
132         /// Transition properties for the transition of View pair having same transition tag.
133         /// </summary>
134         /// <since_tizen> 9 </since_tizen>
135         public Transition Transition
136         {
137             set
138             {
139                 transition = value;
140             }
141             get
142             {
143                 return transition;
144             }
145         }
146
147         /// <summary>
148         /// Pushes a page to Navigator.
149         /// If the page is already in Navigator, then it is not pushed.
150         /// </summary>
151         /// <param name="page">The page to push to Navigator.</param>
152         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
153         /// <since_tizen> 9 </since_tizen>
154         public void PushWithTransition(Page page)
155         {
156             if (!transitionFinished)
157             {
158                 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
159                 return;
160             }
161
162             if (page == null)
163             {
164                 throw new ArgumentNullException(nameof(page), "page should not be null.");
165             }
166
167             //Duplicate page is not pushed.
168             if (navigationPages.Contains(page)) return;
169
170             var topPage = Peek();
171
172             if (!topPage)
173             {
174                 Insert(0, page);
175                 return;
176             }
177
178             navigationPages.Add(page);
179             Add(page);
180             page.Navigator = this;
181
182             //Invoke Page events
183             page.InvokeAppearing();
184             topPage.InvokeDisappearing();
185
186             transitionSet = CreateTransitions(topPage, page, true);
187             transitionSet.Finished += (object sender, EventArgs e) =>
188             {
189                 if (page is DialogPage == false)
190                 {
191                    topPage.SetVisible(false);      
192                 }
193
194                 //Invoke Page events
195                 page.InvokeAppeared();
196                 topPage.InvokeDisappeared();
197             };
198             transitionFinished = false;
199         }
200
201         /// <summary>
202         /// Pops the top page from Navigator.
203         /// </summary>
204         /// <returns>The popped page.</returns>
205         /// <exception cref="InvalidOperationException">Thrown when there is no page in Navigator.</exception>
206         /// <since_tizen> 9 </since_tizen>
207         public Page PopWithTransition()
208         {
209             if (!transitionFinished)
210             {
211                 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
212                 return null;
213             }
214
215             if (navigationPages.Count == 0)
216             {
217                 throw new InvalidOperationException("There is no page in Navigator.");
218             }
219
220             var topPage = Peek();
221
222             if (navigationPages.Count == 1)
223             {
224                 Remove(topPage);
225
226                 //Invoke Popped event
227                 Popped?.Invoke(this, new PoppedEventArgs() { Page = topPage });
228
229                 return topPage;
230             }
231             var newTopPage = navigationPages[navigationPages.Count - 2];
232
233             //Invoke Page events
234             newTopPage.InvokeAppearing();
235             topPage.InvokeDisappearing();
236
237             transitionSet = CreateTransitions(topPage, newTopPage, false);
238             transitionSet.Finished += (object sender, EventArgs e) =>
239             {
240                 Remove(topPage);
241                 topPage.SetVisible(true);
242
243                 //Invoke Page events
244                 newTopPage.InvokeAppeared();
245                 topPage.InvokeDisappeared();
246
247                 //Invoke Popped event
248                 Popped?.Invoke(this, new PoppedEventArgs() { Page = topPage });
249             };
250             transitionFinished = false;
251
252             return topPage;
253         }
254
255         /// <summary>
256         /// Pushes a page to Navigator.
257         /// If the page is already in Navigator, then it is not pushed.
258         /// </summary>
259         /// <param name="page">The page to push to Navigator.</param>
260         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
261         /// <since_tizen> 9 </since_tizen>
262         public void Push(Page page)
263         {
264             if (!transitionFinished)
265             {
266                 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
267                 return;
268             }
269
270             if (page == null)
271             {
272                 throw new ArgumentNullException(nameof(page), "page should not be null.");
273             }
274
275             //Duplicate page is not pushed.
276             if (navigationPages.Contains(page)) return;
277
278             var curTop = Peek();
279
280             if (!curTop)
281             {
282                 Insert(0, page);
283                 return;
284             }
285
286             navigationPages.Add(page);
287             Add(page);
288             page.Navigator = this;
289
290             //Invoke Page events
291             page.InvokeAppearing();
292             curTop.InvokeDisappearing();
293
294             //TODO: The following transition codes will be replaced with view transition.
295             InitializeAnimation();
296
297             if (page is DialogPage == false)
298             {
299                 curAnimation = new Animation(1000);
300                 curAnimation.AnimateTo(curTop, "Opacity", 1.0f, 0, 1000);
301                 curAnimation.EndAction = Animation.EndActions.StopFinal;
302                 curAnimation.Finished += (object sender, EventArgs args) =>
303                 {
304                     curTop.SetVisible(false);
305
306                     //Invoke Page events
307                     curTop.InvokeDisappeared();
308                 };
309                 curAnimation.Play();
310
311                 page.Opacity = 0.0f;
312                 page.SetVisible(true);
313                 newAnimation = new Animation(1000);
314                 newAnimation.AnimateTo(page, "Opacity", 1.0f, 0, 1000);
315                 newAnimation.EndAction = Animation.EndActions.StopFinal;
316                 newAnimation.Finished += (object sender, EventArgs e) =>
317                 {
318                     //Invoke Page events
319                     page.InvokeAppeared();
320                 };
321                 newAnimation.Play();
322             }
323         }
324
325         /// <summary>
326         /// Pops the top page from Navigator.
327         /// </summary>
328         /// <returns>The popped page.</returns>
329         /// <exception cref="InvalidOperationException">Thrown when there is no page in Navigator.</exception>
330         /// <since_tizen> 9 </since_tizen>
331         public Page Pop()
332         {
333             if (!transitionFinished)
334             {
335                 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
336                 return null;
337             }
338
339             if (navigationPages.Count == 0)
340             {
341                 throw new InvalidOperationException("There is no page in Navigator.");
342             }
343
344             var curTop = Peek();
345
346             if (navigationPages.Count == 1)
347             {
348                 Remove(curTop);
349
350                 //Invoke Popped event
351                 Popped?.Invoke(this, new PoppedEventArgs() { Page = curTop });
352
353                 return curTop;
354             }
355
356             var newTop = navigationPages[navigationPages.Count - 2];
357
358             //Invoke Page events
359             newTop.InvokeAppearing();
360             curTop.InvokeDisappearing();
361
362             //TODO: The following transition codes will be replaced with view transition.
363             InitializeAnimation();
364
365             if (curTop is DialogPage == false)
366             {
367                 curAnimation = new Animation(1000);
368                 curAnimation.AnimateTo(curTop, "Opacity", 0.0f, 0, 1000);
369                 curAnimation.EndAction = Animation.EndActions.StopFinal;
370                 curAnimation.Finished += (object sender, EventArgs e) =>
371                 {
372                     //Removes the current top page after transition is finished.
373                     Remove(curTop);
374                     curTop.Opacity = 1.0f;
375
376                     //Invoke Page events
377                     curTop.InvokeDisappeared();
378
379                     //Invoke Popped event
380                     Popped?.Invoke(this, new PoppedEventArgs() { Page = curTop });
381                 };
382                 curAnimation.Play();
383
384                 newTop.Opacity = 1.0f;
385                 newTop.SetVisible(true);
386                 newAnimation = new Animation(1000);
387                 newAnimation.AnimateTo(newTop, "Opacity", 1.0f, 0, 1000);
388                 newAnimation.EndAction = Animation.EndActions.StopFinal;
389                 newAnimation.Finished += (object sender, EventArgs e) =>
390                 {
391                     //Invoke Page events
392                     newTop.InvokeAppeared();
393                 };
394                 newAnimation.Play();
395             }
396             else
397             {
398                 Remove(curTop);
399             }
400
401             return curTop;
402         }
403
404         /// <summary>
405         /// Returns the page of the given index in Navigator.
406         /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
407         /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
408         /// </summary>
409         /// <param name="index">The index of a page in Navigator.</param>
410         /// <returns>The page of the given index in Navigator.</returns>
411         /// <exception cref="ArgumentOutOfRangeException">Thrown when the argument index is less than 0, or greater than the number of pages.</exception>
412         public Page GetPage(int index)
413         {
414             if ((index < 0) || (index > navigationPages.Count))
415             {
416                 throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than or equal to the number of pages.");
417             }
418
419             return navigationPages[index];
420         }
421
422         /// <summary>
423         /// Returns the current index of the given page in Navigator.
424         /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
425         /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
426         /// </summary>
427         /// <param name="page">The page in Navigator.</param>
428         /// <returns>The index of the given page in Navigator. If the given page is not in the Navigator, then -1 is returned.</returns>
429         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
430         /// <since_tizen> 9 </since_tizen>
431         public int IndexOf(Page page)
432         {
433             if (page == null)
434             {
435                 throw new ArgumentNullException(nameof(page), "page should not be null.");
436             }
437
438             for (int i = 0; i < navigationPages.Count; i++)
439             {
440                 if (navigationPages[i] == page)
441                 {
442                     return i;
443                 }
444             }
445
446             return -1;
447         }
448
449         /// <summary>
450         /// Inserts a page at the specified index of Navigator.
451         /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
452         /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
453         /// To find the current index of a page in Navigator, please use IndexOf(page).
454         /// If the page is already in Navigator, then it is not inserted.
455         /// </summary>
456         /// <param name="index">The index of a page in Navigator where the page will be inserted.</param>
457         /// <param name="page">The page to insert to Navigator.</param>
458         /// <exception cref="ArgumentOutOfRangeException">Thrown when the argument index is less than 0, or greater than the number of pages.</exception>
459         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
460         /// <since_tizen> 9 </since_tizen>
461         public void Insert(int index, Page page)
462         {
463             if ((index < 0) || (index > navigationPages.Count))
464             {
465                 throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than or equal to the number of pages.");
466             }
467
468             if (page == null)
469             {
470                 throw new ArgumentNullException(nameof(page), "page should not be null.");
471             }
472
473             //Duplicate page is not pushed.
474             if (navigationPages.Contains(page)) return;
475
476             //TODO: The following transition codes will be replaced with view transition.
477             InitializeAnimation();
478
479             if (index == PageCount)
480             {
481                 page.Opacity = 1.0f;
482                 page.SetVisible(true);
483             }
484             else
485             {
486                 page.SetVisible(false);
487                 page.Opacity = 0.0f;
488             }
489
490             navigationPages.Insert(index, page);
491             Add(page);
492             page.Navigator = this;
493         }
494
495         /// <summary>
496         /// Inserts a page to Navigator before an existing page.
497         /// If the page is already in Navigator, then it is not inserted.
498         /// </summary>
499         /// <param name="before">The existing page, before which a page will be inserted.</param>
500         /// <param name="page">The page to insert to Navigator.</param>
501         /// <exception cref="ArgumentNullException">Thrown when the argument before is null.</exception>
502         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
503         /// <exception cref="ArgumentException">Thrown when the argument before does not exist in Navigator.</exception>
504         /// <since_tizen> 9 </since_tizen>
505         public void InsertBefore(Page before, Page page)
506         {
507             if (before == null)
508             {
509                 throw new ArgumentNullException(nameof(before), "before should not be null.");
510             }
511
512             if (page == null)
513             {
514                 throw new ArgumentNullException(nameof(page), "page should not be null.");
515             }
516
517             //Find the index of before page.
518             int beforeIndex = navigationPages.FindIndex(x => x == before);
519
520             //before does not exist in Navigator.
521             if (beforeIndex == -1)
522             {
523                 throw new ArgumentException("before does not exist in Navigator.", nameof(before));
524             }
525
526             Insert(beforeIndex, page);
527         }
528
529         /// <summary>
530         /// Removes a page from Navigator.
531         /// </summary>
532         /// <param name="page">The page to remove from Navigator.</param>
533         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
534         /// <since_tizen> 9 </since_tizen>
535         public void Remove(Page page)
536         {
537             if (page == null)
538             {
539                 throw new ArgumentNullException(nameof(page), "page should not be null.");
540             }
541
542             //TODO: The following transition codes will be replaced with view transition.
543             InitializeAnimation();
544
545             if ((page == Peek()) && (PageCount >= 2))
546             {
547                 navigationPages[PageCount - 2].Opacity = 1.0f;
548                 navigationPages[PageCount - 2].SetVisible(true);
549             }
550
551             page.Navigator = null;
552             navigationPages.Remove(page);
553             base.Remove(page);
554         }
555
556         /// <summary>
557         /// Removes a page at the specified index of Navigator.
558         /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
559         /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
560         /// To find the current index of a page in Navigator, please use IndexOf(page).
561         /// </summary>
562         /// <param name="index">The index of a page in Navigator where the page will be removed.</param>
563         /// <exception cref="ArgumentOutOfRangeException">Thrown when the index is less than 0, or greater than or equal to the number of pages.</exception>
564         /// <since_tizen> 9 </since_tizen>
565         public void RemoveAt(int index)
566         {
567             if ((index < 0) || (index >= navigationPages.Count))
568             {
569                 throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than the number of pages.");
570             }
571
572             Remove(navigationPages[index]);
573         }
574
575         /// <summary>
576         /// Returns the page at the top of Navigator.
577         /// </summary>
578         /// <returns>The page at the top of Navigator.</returns>
579         /// <since_tizen> 9 </since_tizen>
580         public Page Peek()
581         {
582             if (navigationPages.Count == 0) return null;
583
584             return navigationPages[navigationPages.Count - 1];
585         }
586
587         /// <summary>
588         /// Disposes Navigator and all children on it.
589         /// </summary>
590         /// <param name="type">Dispose type.</param>
591         [EditorBrowsable(EditorBrowsableState.Never)]
592         protected override void Dispose(DisposeTypes type)
593         {
594             if (disposed)
595             {
596                 return;
597             }
598
599             if (type == DisposeTypes.Explicit)
600             {
601                 foreach (Page page in navigationPages)
602                 {
603                     Utility.Dispose(page);
604                 }
605                 navigationPages.Clear();
606
607                 Window window;
608
609                 if (navigatorWindow.TryGetValue(this, out window) == true)
610                 {
611                     navigatorWindow.Remove(this);
612                     windowNavigator.Remove(window);
613                 }
614             }
615
616             base.Dispose(type);
617         }
618
619         /// <summary>
620         /// Returns the default navigator of the given window.
621         /// </summary>
622         /// <returns>The default navigator of the given window.</returns>
623         /// <exception cref="ArgumentNullException">Thrown when the argument window is null.</exception>
624         /// <since_tizen> 9 </since_tizen>
625         public static Navigator GetDefaultNavigator(Window window)
626         {
627             if (window == null)
628             {
629                 throw new ArgumentNullException(nameof(window), "window should not be null.");
630             }
631
632             if (windowNavigator.ContainsKey(window) == true)
633             {
634                 return windowNavigator[window];
635             }
636
637             var defaultNavigator = new Navigator();
638             defaultNavigator.WidthResizePolicy = ResizePolicyType.FillToParent;
639             defaultNavigator.HeightResizePolicy = ResizePolicyType.FillToParent;
640             window.Add(defaultNavigator);
641             windowNavigator.Add(window, defaultNavigator);
642             navigatorWindow.Add(defaultNavigator, window);
643
644             return defaultNavigator;
645         }
646
647         /// <summary>
648         /// Create Transitions between currentTopPage and newTopPage
649         /// </summary>
650         /// <param name="currentTopPage">The top page of Navigator.</param>
651         /// <param name="newTopPage">The new top page after transition.</param>
652         /// <param name="pushTransition">True if this transition is for push new page</param>
653         private TransitionSet CreateTransitions(Page currentTopPage, Page newTopPage, bool pushTransition)
654         {
655             currentTopPage.SetVisible(true);
656             newTopPage.SetVisible(true);
657
658             List<View> taggedViewsInNewTopPage = new List<View>();
659             RetrieveTaggedViews(taggedViewsInNewTopPage, newTopPage, true);
660             List<View> taggedViewsInCurrentTopPage = new List<View>();
661             RetrieveTaggedViews(taggedViewsInCurrentTopPage, currentTopPage, true);
662
663             List<KeyValuePair<View, View>> sameTaggedViewPair = new List<KeyValuePair<View, View>>();
664             foreach(View currentTopPageView in taggedViewsInCurrentTopPage)
665             {
666                 bool findPair = false;
667                 foreach(View newTopPageView in taggedViewsInNewTopPage)
668                 {
669                     if((currentTopPageView.TransitionOptions != null) && (newTopPageView.TransitionOptions != null) &&
670                         currentTopPageView.TransitionOptions?.TransitionTag == newTopPageView.TransitionOptions?.TransitionTag)
671                     {
672                         sameTaggedViewPair.Add(new KeyValuePair<View, View>(currentTopPageView, newTopPageView));
673                         findPair = true;
674                         break;
675                     }
676                 }
677                 if(findPair)
678                 {
679                     taggedViewsInNewTopPage.Remove(sameTaggedViewPair[sameTaggedViewPair.Count - 1].Value);
680                 }
681             }
682             foreach(KeyValuePair<View, View> pair in sameTaggedViewPair)
683             {
684                 taggedViewsInCurrentTopPage.Remove(pair.Key);
685             }
686
687             TransitionSet newTransitionSet = new TransitionSet();
688             foreach(KeyValuePair<View, View> pair in sameTaggedViewPair)
689             {
690                 TransitionItem pairTransition = transition.CreateTransition(pair.Key, pair.Value);
691                 if(pair.Value.TransitionOptions?.TransitionWithChild ?? false)
692                 {
693                     pairTransition.TransitionWithChild = true;
694                 }
695                 newTransitionSet.AddTransition(pairTransition);
696             }
697
698             newTransitionSet.Finished += (object sender, EventArgs e) =>
699             {
700                 if(newTopPage.Layout != null)
701                 {
702                     newTopPage.Layout.RequestLayout();
703                 }
704                 if(currentTopPage.Layout != null)
705                 {
706                     currentTopPage.Layout.RequestLayout();
707                 }
708                 transitionFinished = true;
709                 InvokeTransitionFinished();
710                 transitionSet.Dispose();
711                 currentTopPage.Opacity = 1.0f;
712             };
713
714             if (!pushTransition || newTopPage is DialogPage == false)
715             {
716                 View transitionView = (currentTopPage is ContentPage) ? (currentTopPage as ContentPage).Content : (currentTopPage as DialogPage).Content;
717                 if (currentTopPage.DisappearingTransition != null && transitionView != null)
718                 {
719                     TransitionItemBase disappearingTransition = currentTopPage.DisappearingTransition.CreateTransition(transitionView, false);
720                     disappearingTransition.TransitionWithChild = true;
721                     newTransitionSet.AddTransition(disappearingTransition);
722                 }
723                 else
724                 {
725                     currentTopPage.SetVisible(false);
726                 }
727             }
728             if (pushTransition || currentTopPage is DialogPage == false)
729             {
730                 View transitionView = (newTopPage is ContentPage) ? (newTopPage as ContentPage).Content : (newTopPage as DialogPage).Content;
731                 if (newTopPage.AppearingTransition != null && transitionView != null)
732                 {
733                     TransitionItemBase appearingTransition = newTopPage.AppearingTransition.CreateTransition(transitionView, true);
734                     appearingTransition.TransitionWithChild = true;
735                     newTransitionSet.AddTransition(appearingTransition);
736                 }
737             }
738
739             newTransitionSet.Play();
740
741             return newTransitionSet;
742         }
743
744         /// <summary>
745         /// Retrieve Tagged Views in the view tree.
746         /// </summary>
747         /// <param name="taggedViews">Returned tagged view list..</param>
748         /// <param name="view">Root View to get tagged child View.</param>
749         /// <param name="isPage">Flag to check current View is page or not</param>
750         private void RetrieveTaggedViews(List<View> taggedViews, View view, bool isPage)
751         {
752             if (!isPage)
753             {
754                 if (!string.IsNullOrEmpty(view.TransitionOptions?.TransitionTag))
755                 {
756                     taggedViews.Add((view as View));
757                 }
758
759                 if (view.ChildCount == 0)
760                 {
761                     return;
762                 }
763
764                 if (view.TransitionOptions?.TransitionWithChild ?? false)
765                 {
766                     return;
767                 }
768             }
769             foreach (View child in view.Children)
770             {
771                 RetrieveTaggedViews(taggedViews, child, false);
772             }
773         }
774
775         internal void InvokeTransitionFinished()
776         {
777             TransitionFinished?.Invoke(this, new EventArgs());
778         }
779
780         //TODO: The following transition codes will be replaced with view transition.
781         private void InitializeAnimation()
782         {
783             if (curAnimation != null)
784             {
785                 curAnimation.Stop();
786                 curAnimation.Clear();
787                 curAnimation = null;
788             }
789
790             if (newAnimation != null)
791             {
792                 newAnimation.Stop();
793                 newAnimation.Clear();
794                 newAnimation = null;
795             }
796         }
797     }
798 }