2 * Copyright(c) 2021 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 using System.Collections.Generic;
20 using System.ComponentModel;
21 using Tizen.NUI.BaseComponents;
22 using Tizen.NUI.Binding;
24 namespace Tizen.NUI.Components
27 /// PoppedEventArgs is a class to record <see cref="Navigator.Popped"/> event arguments which will be sent to user.
29 /// <since_tizen> 9 </since_tizen>
30 public class PoppedEventArgs : EventArgs
33 /// Page popped by Navigator.
35 /// <since_tizen> 9 </since_tizen>
36 public Page Page { get; internal set; }
40 /// The Navigator is a class which navigates pages with stack methods such as Push and Pop.
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.
52 /// Navigator navigator = new Navigator()
54 /// TimePeriod = new TimePeriod(500),
55 /// AlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.EaseInOutSine)
58 /// View view = new View()
60 /// TransitionOptions = new TransitionOptions()
62 /// /* Set properties for the transition of this View */
66 /// ContentPage newPage = new ContentPage()
71 /// Navigator.PushWithTransition(newPage);
74 /// <since_tizen> 9 </since_tizen>
75 public class Navigator : Control
78 /// TransitionProperty
80 [EditorBrowsable(EditorBrowsableState.Never)]
81 public static readonly BindableProperty TransitionProperty = BindableProperty.Create(nameof(Transition), typeof(Transition), typeof(Navigator), null, propertyChanged: (bindable, oldValue, newValue) =>
83 var instance = (Navigator)bindable;
86 instance.InternalTransition = newValue as Transition;
89 defaultValueCreator: (bindable) =>
91 var instance = (Navigator)bindable;
92 return instance.InternalTransition;
96 /// EnableBackNavigationProperty
98 [EditorBrowsable(EditorBrowsableState.Never)]
99 public static readonly BindableProperty EnableBackNavigationProperty = BindableProperty.Create(nameof(EnableBackNavigation), typeof(bool), typeof(Navigator), default(bool), propertyChanged: (bindable, oldValue, newValue) =>
101 var instance = (Navigator)bindable;
102 if (newValue != null)
104 instance.InternalEnableBackNavigation = (bool)newValue;
107 defaultValueCreator: (bindable) =>
109 var instance = (Navigator)bindable;
110 return instance.InternalEnableBackNavigation;
113 private const int DefaultTransitionDuration = 300;
115 //This will be replaced with view transition class instance.
116 private Animation curAnimation = null;
118 //This will be replaced with view transition class instance.
119 private Animation newAnimation = null;
121 private TransitionSet transitionSet = null;
123 private Transition transition = new Transition()
125 TimePeriod = new TimePeriod(DefaultTransitionDuration),
126 AlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.Default),
129 private bool transitionFinished = true;
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>();
136 private List<Page> navigationPages = new List<Page>();
138 private bool enableBackNavigation = true;
140 private Window parentWindow;
142 private void OnWindowKeyEvent(object sender, Window.KeyEventArgs e)
144 if (!EnableBackNavigation)
149 if ((e.Key.State == Key.StateType.Up) && ((e.Key.KeyPressedName == "Escape") || (e.Key.KeyPressedName == "BackSpace") || (e.Key.KeyPressedName == "XF86Back")))
151 OnBackNavigation(new BackNavigationEventArgs());
155 private void OnAddedToWindow(object sender, EventArgs e)
157 parentWindow = Window.Get(this);
158 parentWindow.KeyEvent += OnWindowKeyEvent;
161 private void OnRemovedFromWindow(object sender, EventArgs e)
163 parentWindow.KeyEvent -= OnWindowKeyEvent;
167 private void Initialize()
169 Layout = new AbsoluteLayout();
171 AddedToWindow += OnAddedToWindow;
172 RemovedFromWindow += OnRemovedFromWindow;
176 /// Creates a new instance of a Navigator.
178 /// <since_tizen> 9 </since_tizen>
179 public Navigator() : base()
185 /// Creates a new instance of Navigator with style.
187 /// <param name="style">Creates Navigator by special style defined in UX.</param>
188 [EditorBrowsable(EditorBrowsableState.Never)]
189 public Navigator(string style) : base(style)
195 /// Creates a new instance of a Navigator with style.
197 /// <param name="style">A style applied to the newly created Navigator.</param>
198 [EditorBrowsable(EditorBrowsableState.Never)]
199 public Navigator(ControlStyle style) : base(style)
205 [EditorBrowsable(EditorBrowsableState.Never)]
206 public override void OnInitialize()
210 AccessibilityRole = Role.PageTabList;
214 /// An event fired when Transition has been finished.
216 /// <since_tizen> 9 </since_tizen>
217 public event EventHandler<EventArgs> TransitionFinished;
220 /// An event fired when Pop of a page has been finished.
223 /// When you free resources in the Popped event handler, please make sure if the popped page is the page you find.
225 /// <since_tizen> 9 </since_tizen>
226 public event EventHandler<PoppedEventArgs> Popped;
229 /// Returns the count of pages in Navigator.
231 /// <since_tizen> 9 </since_tizen>
232 public int PageCount => navigationPages.Count;
235 /// Transition properties for the transition of View pair having same transition tag.
237 /// <since_tizen> 9 </since_tizen>
238 public Transition Transition
242 return GetValue(TransitionProperty) as Transition;
246 SetValue(TransitionProperty, value);
247 NotifyPropertyChanged();
250 private Transition InternalTransition
263 /// Pushes a page to Navigator.
264 /// If the page is already in Navigator, then it is not pushed.
266 /// <param name="page">The page to push to Navigator.</param>
267 /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
268 /// <since_tizen> 9 </since_tizen>
269 public void PushWithTransition(Page page)
271 if (!transitionFinished)
273 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
279 throw new ArgumentNullException(nameof(page), "page should not be null.");
282 //Duplicate page is not pushed.
283 if (navigationPages.Contains(page)) return;
285 var topPage = Peek();
293 navigationPages.Add(page);
295 page.Navigator = this;
298 page.InvokeAppearing();
299 topPage.InvokeDisappearing();
300 topPage.SaveKeyFocus();
302 transitionSet = CreateTransitions(topPage, page, true);
303 transitionSet.Finished += (object sender, EventArgs e) =>
305 if (page is DialogPage == false)
307 topPage.SetVisible(false);
310 // Need to update Content of the new page
311 ShowContentOfPage(page);
314 page.InvokeAppeared();
315 page.RestoreKeyFocus();
317 topPage.InvokeDisappeared();
318 NotifyAccessibilityStatesChangeOfPages(topPage, page);
320 transitionFinished = false;
324 /// Pops the top page from Navigator.
326 /// <returns>The popped page.</returns>
327 /// <exception cref="InvalidOperationException">Thrown when there is no page in Navigator.</exception>
328 /// <since_tizen> 9 </since_tizen>
329 public Page PopWithTransition()
331 if (!transitionFinished)
333 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
337 if (navigationPages.Count == 0)
339 throw new InvalidOperationException("There is no page in Navigator.");
342 var topPage = Peek();
344 if (navigationPages.Count == 1)
348 //Invoke Popped event
349 Popped?.Invoke(this, new PoppedEventArgs() { Page = topPage });
353 var newTopPage = navigationPages[navigationPages.Count - 2];
356 newTopPage.InvokeAppearing();
357 topPage.InvokeDisappearing();
358 topPage.SaveKeyFocus();
360 transitionSet = CreateTransitions(topPage, newTopPage, false);
361 transitionSet.Finished += (object sender, EventArgs e) =>
364 topPage.SetVisible(true);
366 // Need to update Content of the new page
367 ShowContentOfPage(newTopPage);
370 newTopPage.InvokeAppeared();
371 newTopPage.RestoreKeyFocus();
373 topPage.InvokeDisappeared();
375 //Invoke Popped event
376 Popped?.Invoke(this, new PoppedEventArgs() { Page = topPage });
378 transitionFinished = false;
384 /// Pushes a page to Navigator.
385 /// If the page is already in Navigator, then it is not pushed.
387 /// <param name="page">The page to push to Navigator.</param>
388 /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
389 /// <since_tizen> 9 </since_tizen>
390 public void Push(Page page)
392 if (!transitionFinished)
394 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
400 throw new ArgumentNullException(nameof(page), "page should not be null.");
403 //Duplicate page is not pushed.
404 if (navigationPages.Contains(page)) return;
414 navigationPages.Add(page);
416 page.Navigator = this;
419 page.InvokeAppearing();
420 curTop.InvokeDisappearing();
422 curTop.SaveKeyFocus();
424 //TODO: The following transition codes will be replaced with view transition.
425 InitializeAnimation();
427 if (page is DialogPage == false)
429 curAnimation = new Animation(DefaultTransitionDuration);
430 curAnimation.AnimateTo(curTop, "PositionX", 0.0f, 0, DefaultTransitionDuration);
431 curAnimation.EndAction = Animation.EndActions.StopFinal;
432 curAnimation.Finished += (object sender, EventArgs args) =>
434 curTop.SetVisible(false);
437 curTop.InvokeDisappeared();
441 page.PositionX = SizeWidth;
442 page.SetVisible(true);
443 // Set Content visible because it was hidden by HideContentOfPage.
444 (page as ContentPage)?.Content?.SetVisible(true);
446 newAnimation = new Animation(DefaultTransitionDuration);
447 newAnimation.AnimateTo(page, "PositionX", 0.0f, 0, DefaultTransitionDuration);
448 newAnimation.EndAction = Animation.EndActions.StopFinal;
449 newAnimation.Finished += (object sender, EventArgs e) =>
451 // Need to update Content of the new page
452 ShowContentOfPage(page);
455 page.InvokeAppeared();
456 NotifyAccessibilityStatesChangeOfPages(curTop, page);
458 page.RestoreKeyFocus();
464 ShowContentOfPage(page);
465 page.RestoreKeyFocus();
470 /// Pops the top page from Navigator.
472 /// <returns>The popped page.</returns>
473 /// <exception cref="InvalidOperationException">Thrown when there is no page in Navigator.</exception>
474 /// <since_tizen> 9 </since_tizen>
477 if (!transitionFinished)
479 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
483 if (navigationPages.Count == 0)
485 throw new InvalidOperationException("There is no page in Navigator.");
490 if (navigationPages.Count == 1)
494 //Invoke Popped event
495 Popped?.Invoke(this, new PoppedEventArgs() { Page = curTop });
500 var newTop = navigationPages[navigationPages.Count - 2];
503 newTop.InvokeAppearing();
504 curTop.InvokeDisappearing();
505 curTop.SaveKeyFocus();
507 //TODO: The following transition codes will be replaced with view transition.
508 InitializeAnimation();
510 if (curTop is DialogPage == false)
512 curAnimation = new Animation(DefaultTransitionDuration);
513 curAnimation.AnimateTo(curTop, "PositionX", SizeWidth, 0, DefaultTransitionDuration);
514 curAnimation.EndAction = Animation.EndActions.StopFinal;
515 curAnimation.Finished += (object sender, EventArgs e) =>
517 //Removes the current top page after transition is finished.
520 curTop.PositionX = 0.0f;
523 curTop.InvokeDisappeared();
525 //Invoke Popped event
526 Popped?.Invoke(this, new PoppedEventArgs() { Page = curTop });
530 newTop.SetVisible(true);
531 // Set Content visible because it was hidden by HideContentOfPage.
532 (newTop as ContentPage)?.Content?.SetVisible(true);
534 newAnimation = new Animation(DefaultTransitionDuration);
535 newAnimation.AnimateTo(newTop, "PositionX", 0.0f, 0, DefaultTransitionDuration);
536 newAnimation.EndAction = Animation.EndActions.StopFinal;
537 newAnimation.Finished += (object sender, EventArgs e) =>
539 // Need to update Content of the new page
540 ShowContentOfPage(newTop);
543 newTop.InvokeAppeared();
545 newTop.RestoreKeyFocus();
552 newTop.RestoreKeyFocus();
559 /// Returns the page of the given index in Navigator.
560 /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
561 /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
563 /// <param name="index">The index of a page in Navigator.</param>
564 /// <returns>The page of the given index in Navigator.</returns>
565 /// <exception cref="ArgumentOutOfRangeException">Thrown when the argument index is less than 0, or greater than the number of pages.</exception>
566 public Page GetPage(int index)
568 if ((index < 0) || (index > navigationPages.Count))
570 throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than or equal to the number of pages.");
573 return navigationPages[index];
577 /// Returns the current index of the given page in Navigator.
578 /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
579 /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
581 /// <param name="page">The page in Navigator.</param>
582 /// <returns>The index of the given page in Navigator. If the given page is not in the Navigator, then -1 is returned.</returns>
583 /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
584 /// <since_tizen> 9 </since_tizen>
585 public int IndexOf(Page page)
589 throw new ArgumentNullException(nameof(page), "page should not be null.");
592 for (int i = 0; i < navigationPages.Count; i++)
594 if (navigationPages[i] == page)
604 /// Inserts a page at the specified index of Navigator.
605 /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
606 /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
607 /// To find the current index of a page in Navigator, please use IndexOf(page).
608 /// If the page is already in Navigator, then it is not inserted.
610 /// <param name="index">The index of a page in Navigator where the page will be inserted.</param>
611 /// <param name="page">The page to insert to Navigator.</param>
612 /// <exception cref="ArgumentOutOfRangeException">Thrown when the argument index is less than 0, or greater than the number of pages.</exception>
613 /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
614 /// <since_tizen> 9 </since_tizen>
615 public void Insert(int index, Page page)
617 if ((index < 0) || (index > navigationPages.Count))
619 throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than or equal to the number of pages.");
624 throw new ArgumentNullException(nameof(page), "page should not be null.");
627 //Duplicate page is not pushed.
628 if (navigationPages.Contains(page)) return;
630 //TODO: The following transition codes will be replaced with view transition.
631 InitializeAnimation();
633 ShowContentOfPage(page);
635 if (index == PageCount)
637 page.SetVisible(true);
641 page.SetVisible(false);
644 navigationPages.Insert(index, page);
646 page.SiblingOrder = index;
647 page.Navigator = this;
648 if (index == PageCount - 1)
652 NotifyAccessibilityStatesChangeOfPages(navigationPages[PageCount - 2], page);
656 NotifyAccessibilityStatesChangeOfPages(null, page);
662 /// Inserts a page to Navigator before an existing page.
663 /// If the page is already in Navigator, then it is not inserted.
665 /// <param name="before">The existing page, before which a page will be inserted.</param>
666 /// <param name="page">The page to insert to Navigator.</param>
667 /// <exception cref="ArgumentNullException">Thrown when the argument before is null.</exception>
668 /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
669 /// <exception cref="ArgumentException">Thrown when the argument before does not exist in Navigator.</exception>
670 /// <since_tizen> 9 </since_tizen>
671 public void InsertBefore(Page before, Page page)
675 throw new ArgumentNullException(nameof(before), "before should not be null.");
680 throw new ArgumentNullException(nameof(page), "page should not be null.");
683 //Find the index of before page.
684 int beforeIndex = navigationPages.FindIndex(x => x == before);
686 //before does not exist in Navigator.
687 if (beforeIndex == -1)
689 throw new ArgumentException("before does not exist in Navigator.", nameof(before));
692 Insert(beforeIndex, page);
696 /// Removes a page from Navigator.
698 /// <param name="page">The page to remove from Navigator.</param>
699 /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
700 /// <since_tizen> 9 </since_tizen>
701 public void Remove(Page page)
705 throw new ArgumentNullException(nameof(page), "page should not be null.");
708 //TODO: The following transition codes will be replaced with view transition.
709 InitializeAnimation();
711 HideContentOfPage(page);
717 navigationPages[PageCount - 2].SetVisible(true);
718 NotifyAccessibilityStatesChangeOfPages(page, navigationPages[PageCount - 2]);
720 else if (PageCount == 1)
722 NotifyAccessibilityStatesChangeOfPages(page, null);
725 page.Navigator = null;
726 navigationPages.Remove(page);
731 /// Removes a page at the specified index of Navigator.
732 /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
733 /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
734 /// To find the current index of a page in Navigator, please use IndexOf(page).
736 /// <param name="index">The index of a page in Navigator where the page will be removed.</param>
737 /// <exception cref="ArgumentOutOfRangeException">Thrown when the index is less than 0, or greater than or equal to the number of pages.</exception>
738 /// <since_tizen> 9 </since_tizen>
739 public void RemoveAt(int index)
741 if ((index < 0) || (index >= navigationPages.Count))
743 throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than the number of pages.");
746 Remove(navigationPages[index]);
750 /// Returns the page at the top of Navigator.
752 /// <returns>The page at the top of Navigator.</returns>
753 /// <since_tizen> 9 </since_tizen>
756 if (navigationPages.Count == 0) return null;
758 return navigationPages[navigationPages.Count - 1];
762 /// Gets or sets if Navigator proceeds back navigation when back button or back key is pressed and released.
763 /// Back navigation pops the peek page if Navigator has more than one page.
764 /// If Navigator has only one page, then the current program is exited.
766 [EditorBrowsable(EditorBrowsableState.Never)]
767 public bool EnableBackNavigation
771 return (bool)GetValue(EnableBackNavigationProperty);
775 SetValue(EnableBackNavigationProperty, value);
776 NotifyPropertyChanged();
780 private bool InternalEnableBackNavigation
784 enableBackNavigation = value;
788 return enableBackNavigation;
793 /// Disposes Navigator and all children on it.
795 /// <param name="type">Dispose type.</param>
796 [EditorBrowsable(EditorBrowsableState.Never)]
797 protected override void Dispose(DisposeTypes type)
804 if (type == DisposeTypes.Explicit)
806 foreach (Page page in navigationPages)
808 Utility.Dispose(page);
810 navigationPages.Clear();
814 if (navigatorWindow.TryGetValue(this, out window) == true)
816 navigatorWindow.Remove(this);
817 windowNavigator.Remove(window);
820 AddedToWindow -= OnAddedToWindow;
821 RemovedFromWindow -= OnRemovedFromWindow;
828 /// Returns the default navigator of the given window.
830 /// <returns>The default navigator of the given window.</returns>
831 /// <exception cref="ArgumentNullException">Thrown when the argument window is null.</exception>
832 /// <since_tizen> 9 </since_tizen>
833 public static Navigator GetDefaultNavigator(Window window)
837 throw new ArgumentNullException(nameof(window), "window should not be null.");
840 if (windowNavigator.ContainsKey(window) == true)
842 return windowNavigator[window];
845 var defaultNavigator = new Navigator();
846 defaultNavigator.WidthResizePolicy = ResizePolicyType.FillToParent;
847 defaultNavigator.HeightResizePolicy = ResizePolicyType.FillToParent;
848 window.Add(defaultNavigator);
849 windowNavigator.Add(window, defaultNavigator);
850 navigatorWindow.Add(defaultNavigator, window);
852 return defaultNavigator;
856 /// Sets the default navigator of the given window.
857 /// SetDefaultNavigator does not remove the previous default navigator from the window.
858 /// Therefore, if a user does not want to show the previous default navigator, then it should be removed from the window manually.
860 /// <exception cref="ArgumentNullException">Thrown when the argument window is null.</exception>
861 /// <exception cref="ArgumentNullException">Thrown when the argument newNavigator is null.</exception>
862 [EditorBrowsable(EditorBrowsableState.Never)]
863 public static void SetDefaultNavigator(Window window, Navigator newNavigator)
867 throw new ArgumentNullException(nameof(window), "window should not be null.");
870 if (newNavigator == null)
872 throw new ArgumentNullException(nameof(newNavigator), "newNavigator should not be null.");
875 if (windowNavigator.ContainsKey(window) == true)
877 var oldNavigator = windowNavigator[window];
878 if (oldNavigator == newNavigator)
883 navigatorWindow.Remove(oldNavigator);
884 windowNavigator.Remove(window);
887 window.Add(newNavigator);
888 windowNavigator.Add(window, newNavigator);
889 navigatorWindow.Add(newNavigator, window);
893 /// Called when the back navigation is started.
894 /// Back navigation pops the peek page if Navigator has more than one page.
895 /// If Navigator has only one page, then the current program is exited.
897 /// <param name="eventArgs">The back navigation information.</param>
898 [EditorBrowsable(EditorBrowsableState.Never)]
899 protected virtual void OnBackNavigation(BackNavigationEventArgs eventArgs)
903 if (Peek().EnableBackNavigation)
905 Peek().NavigateBack();
910 NUIApplication.Current?.Exit();
915 /// Called when the back navigation is required outside Navigator.
917 internal void NavigateBack()
919 OnBackNavigation(new BackNavigationEventArgs());
923 /// Create Transitions between currentTopPage and newTopPage
925 /// <param name="currentTopPage">The top page of Navigator.</param>
926 /// <param name="newTopPage">The new top page after transition.</param>
927 /// <param name="pushTransition">True if this transition is for push new page</param>
928 private TransitionSet CreateTransitions(Page currentTopPage, Page newTopPage, bool pushTransition)
930 currentTopPage.SetVisible(true);
931 // Set Content visible because it was hidden by HideContentOfPage.
932 (currentTopPage as ContentPage)?.Content?.SetVisible(true);
934 newTopPage.SetVisible(true);
935 // Set Content visible because it was hidden by HideContentOfPage.
936 (newTopPage as ContentPage)?.Content?.SetVisible(true);
938 List<View> taggedViewsInNewTopPage = new List<View>();
939 RetrieveTaggedViews(taggedViewsInNewTopPage, newTopPage, true);
940 List<View> taggedViewsInCurrentTopPage = new List<View>();
941 RetrieveTaggedViews(taggedViewsInCurrentTopPage, currentTopPage, true);
943 List<KeyValuePair<View, View>> sameTaggedViewPair = new List<KeyValuePair<View, View>>();
944 foreach (View currentTopPageView in taggedViewsInCurrentTopPage)
946 bool findPair = false;
947 foreach (View newTopPageView in taggedViewsInNewTopPage)
949 if ((currentTopPageView.TransitionOptions != null) && (newTopPageView.TransitionOptions != null) &&
950 currentTopPageView.TransitionOptions?.TransitionTag == newTopPageView.TransitionOptions?.TransitionTag)
952 sameTaggedViewPair.Add(new KeyValuePair<View, View>(currentTopPageView, newTopPageView));
959 taggedViewsInNewTopPage.Remove(sameTaggedViewPair[sameTaggedViewPair.Count - 1].Value);
962 foreach (KeyValuePair<View, View> pair in sameTaggedViewPair)
964 taggedViewsInCurrentTopPage.Remove(pair.Key);
967 TransitionSet newTransitionSet = new TransitionSet();
968 foreach (KeyValuePair<View, View> pair in sameTaggedViewPair)
970 TransitionItem pairTransition = transition.CreateTransition(pair.Key, pair.Value, pushTransition);
971 if (pair.Value.TransitionOptions?.TransitionWithChild ?? false)
973 pairTransition.TransitionWithChild = true;
975 newTransitionSet.AddTransition(pairTransition);
978 newTransitionSet.Finished += (object sender, EventArgs e) =>
980 if (newTopPage.Layout != null)
982 newTopPage.Layout.RequestLayout();
984 if (currentTopPage.Layout != null)
986 currentTopPage.Layout.RequestLayout();
988 transitionFinished = true;
989 InvokeTransitionFinished();
990 transitionSet.Dispose();
993 if (!pushTransition || newTopPage is DialogPage == false)
995 View transitionView = (currentTopPage is ContentPage) ? (currentTopPage as ContentPage).Content : (currentTopPage as DialogPage).Content;
996 if (currentTopPage.DisappearingTransition != null && transitionView != null)
998 TransitionItemBase disappearingTransition = currentTopPage.DisappearingTransition.CreateTransition(transitionView, false);
999 disappearingTransition.TransitionWithChild = true;
1000 newTransitionSet.AddTransition(disappearingTransition);
1004 currentTopPage.SetVisible(false);
1007 if (pushTransition || currentTopPage is DialogPage == false)
1009 View transitionView = (newTopPage is ContentPage) ? (newTopPage as ContentPage).Content : (newTopPage as DialogPage).Content;
1010 if (newTopPage.AppearingTransition != null && transitionView != null)
1012 TransitionItemBase appearingTransition = newTopPage.AppearingTransition.CreateTransition(transitionView, true);
1013 appearingTransition.TransitionWithChild = true;
1014 newTransitionSet.AddTransition(appearingTransition);
1018 newTransitionSet.Play();
1020 return newTransitionSet;
1024 /// Retrieve Tagged Views in the view tree.
1026 /// <param name="taggedViews">Returned tagged view list..</param>
1027 /// <param name="view">Root View to get tagged child View.</param>
1028 /// <param name="isRoot">Flag to check current View is page or not</param>
1029 private void RetrieveTaggedViews(List<View> taggedViews, View view, bool isRoot)
1031 if (!isRoot && view.TransitionOptions != null)
1033 if (!string.IsNullOrEmpty(view.TransitionOptions?.TransitionTag))
1035 taggedViews.Add((view as View));
1036 if (view.TransitionOptions.TransitionWithChild)
1044 foreach (View child in view.Children)
1046 RetrieveTaggedViews(taggedViews, child, false);
1051 /// Notify accessibility states change of pages.
1053 /// <param name="disappearedPage">Disappeared page</param>
1054 /// <param name="appearedPage">Appeared page</param>
1055 private void NotifyAccessibilityStatesChangeOfPages(Page disappearedPage, Page appearedPage)
1057 if (disappearedPage != null)
1059 disappearedPage.UnregisterDefaultLabel();
1060 //We can call disappearedPage.NotifyAccessibilityStatesChange
1061 //To reduce accessibility events, we are using currently highlighted view instead
1062 View curHighlightedView = Accessibility.Accessibility.GetCurrentlyHighlightedView();
1063 if (curHighlightedView != null)
1065 curHighlightedView.NotifyAccessibilityStatesChange(new AccessibilityStates(AccessibilityState.Visible, AccessibilityState.Showing), AccessibilityStatesNotifyMode.Single);
1069 if (appearedPage != null)
1071 appearedPage.RegisterDefaultLabel();
1072 appearedPage.NotifyAccessibilityStatesChange(new AccessibilityStates(AccessibilityState.Visible, AccessibilityState.Showing), AccessibilityStatesNotifyMode.Single);
1076 internal void InvokeTransitionFinished()
1078 TransitionFinished?.Invoke(this, new EventArgs());
1081 //TODO: The following transition codes will be replaced with view transition.
1082 private void InitializeAnimation()
1084 if (curAnimation != null)
1086 curAnimation.Stop();
1087 curAnimation.Clear();
1088 curAnimation = null;
1091 if (newAnimation != null)
1093 newAnimation.Stop();
1094 newAnimation.Clear();
1095 newAnimation = null;
1099 // Show and Register Content of Page to Accessibility bridge
1100 private void ShowContentOfPage(Page page)
1102 View content = (page is DialogPage) ? (page as DialogPage)?.Content : (page as ContentPage)?.Content;
1103 if (content != null)
1105 content.Show(); // Calls RegisterDefaultLabel()
1109 // Hide and Remove Content of Page from Accessibility bridge
1110 private void HideContentOfPage(Page page)
1112 View content = (page is DialogPage) ? (page as DialogPage)?.Content : (page as ContentPage)?.Content;
1113 if (content != null)
1115 content.Hide(); // Calls UnregisterDefaultLabel()
1121 /// BackNavigationEventArgs is a class to record back navigation event arguments which will sent to user.
1123 [EditorBrowsable(EditorBrowsableState.Never)]
1124 public class BackNavigationEventArgs : EventArgs