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 if (null != parentWindow)
160 parentWindow.KeyEvent += OnWindowKeyEvent;
164 private void OnRemovedFromWindow(object sender, EventArgs e)
166 if (null != parentWindow)
168 parentWindow.KeyEvent -= OnWindowKeyEvent;
173 private void Initialize()
175 Layout = new AbsoluteLayout();
177 AddedToWindow += OnAddedToWindow;
178 RemovedFromWindow += OnRemovedFromWindow;
182 /// Creates a new instance of a Navigator.
184 /// <since_tizen> 9 </since_tizen>
185 public Navigator() : base()
191 /// Creates a new instance of Navigator with style.
193 /// <param name="style">Creates Navigator by special style defined in UX.</param>
194 [EditorBrowsable(EditorBrowsableState.Never)]
195 public Navigator(string style) : base(style)
201 /// Creates a new instance of a Navigator with style.
203 /// <param name="style">A style applied to the newly created Navigator.</param>
204 [EditorBrowsable(EditorBrowsableState.Never)]
205 public Navigator(ControlStyle style) : base(style)
211 [EditorBrowsable(EditorBrowsableState.Never)]
212 public override void OnInitialize()
216 AccessibilityRole = Role.PageTabList;
220 /// An event fired when Transition has been finished.
222 /// <since_tizen> 9 </since_tizen>
223 public event EventHandler<EventArgs> TransitionFinished;
226 /// An event fired when Pop of a page has been finished.
229 /// When you free resources in the Popped event handler, please make sure if the popped page is the page you find.
231 /// <since_tizen> 9 </since_tizen>
232 public event EventHandler<PoppedEventArgs> Popped;
235 /// Returns the count of pages in Navigator.
237 /// <since_tizen> 9 </since_tizen>
238 public int PageCount => navigationPages.Count;
241 /// Transition properties for the transition of View pair having same transition tag.
243 /// <since_tizen> 9 </since_tizen>
244 public Transition Transition
248 return GetValue(TransitionProperty) as Transition;
252 SetValue(TransitionProperty, value);
253 NotifyPropertyChanged();
256 private Transition InternalTransition
269 /// Pushes a page to Navigator.
270 /// If the page is already in Navigator, then it is not pushed.
272 /// <param name="page">The page to push to Navigator.</param>
273 /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
274 /// <since_tizen> 9 </since_tizen>
275 public void PushWithTransition(Page page)
277 if (!transitionFinished)
279 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
285 throw new ArgumentNullException(nameof(page), "page should not be null.");
288 //Duplicate page is not pushed.
289 if (navigationPages.Contains(page)) return;
291 var topPage = Peek();
299 navigationPages.Add(page);
301 page.Navigator = this;
304 page.InvokeAppearing();
305 topPage.InvokeDisappearing();
306 topPage.SaveKeyFocus();
308 transitionSet = CreateTransitions(topPage, page, true);
309 transitionSet.Finished += (object sender, EventArgs e) =>
311 if (page is DialogPage == false)
313 topPage.SetVisible(false);
316 // Need to update Content of the new page
317 ShowContentOfPage(page);
320 page.InvokeAppeared();
321 page.RestoreKeyFocus();
323 topPage.InvokeDisappeared();
324 NotifyAccessibilityStatesChangeOfPages(topPage, page);
326 transitionFinished = false;
330 /// Pops the top page from Navigator.
332 /// <returns>The popped page.</returns>
333 /// <exception cref="InvalidOperationException">Thrown when there is no page in Navigator.</exception>
334 /// <since_tizen> 9 </since_tizen>
335 public Page PopWithTransition()
337 if (!transitionFinished)
339 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
343 if (navigationPages.Count == 0)
345 throw new InvalidOperationException("There is no page in Navigator.");
348 var topPage = Peek();
350 if (navigationPages.Count == 1)
354 //Invoke Popped event
355 Popped?.Invoke(this, new PoppedEventArgs() { Page = topPage });
359 var newTopPage = navigationPages[navigationPages.Count - 2];
362 newTopPage.InvokeAppearing();
363 topPage.InvokeDisappearing();
364 topPage.SaveKeyFocus();
366 transitionSet = CreateTransitions(topPage, newTopPage, false);
367 transitionSet.Finished += (object sender, EventArgs e) =>
370 topPage.SetVisible(true);
372 // Need to update Content of the new page
373 ShowContentOfPage(newTopPage);
376 newTopPage.InvokeAppeared();
377 newTopPage.RestoreKeyFocus();
379 topPage.InvokeDisappeared();
381 //Invoke Popped event
382 Popped?.Invoke(this, new PoppedEventArgs() { Page = topPage });
384 transitionFinished = false;
390 /// Pushes a page to Navigator.
391 /// If the page is already in Navigator, then it is not pushed.
393 /// <param name="page">The page to push to Navigator.</param>
394 /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
395 /// <since_tizen> 9 </since_tizen>
396 public void Push(Page page)
398 if (!transitionFinished)
400 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
406 throw new ArgumentNullException(nameof(page), "page should not be null.");
409 //Duplicate page is not pushed.
410 if (navigationPages.Contains(page)) return;
412 //TODO: The following transition codes will be replaced with view transition.
413 InitializeAnimation();
423 navigationPages.Add(page);
425 page.Navigator = this;
428 page.InvokeAppearing();
429 curTop.InvokeDisappearing();
431 curTop.SaveKeyFocus();
433 if (page is DialogPage == false)
435 curAnimation = new Animation(DefaultTransitionDuration);
436 curAnimation.AnimateTo(curTop, "PositionX", 0.0f, 0, DefaultTransitionDuration);
437 curAnimation.EndAction = Animation.EndActions.StopFinal;
438 curAnimation.Finished += (object sender, EventArgs args) =>
440 curTop.SetVisible(false);
443 curTop.InvokeDisappeared();
447 page.PositionX = SizeWidth;
448 page.SetVisible(true);
449 // Set Content visible because it was hidden by HideContentOfPage.
450 (page as ContentPage)?.Content?.SetVisible(true);
452 newAnimation = new Animation(DefaultTransitionDuration);
453 newAnimation.AnimateTo(page, "PositionX", 0.0f, 0, DefaultTransitionDuration);
454 newAnimation.EndAction = Animation.EndActions.StopFinal;
455 newAnimation.Finished += (object sender, EventArgs e) =>
457 // Need to update Content of the new page
458 ShowContentOfPage(page);
461 page.InvokeAppeared();
462 NotifyAccessibilityStatesChangeOfPages(curTop, page);
464 page.RestoreKeyFocus();
470 ShowContentOfPage(page);
471 page.RestoreKeyFocus();
476 /// Pops the top page from Navigator.
478 /// <returns>The popped page.</returns>
479 /// <exception cref="InvalidOperationException">Thrown when there is no page in Navigator.</exception>
480 /// <since_tizen> 9 </since_tizen>
483 if (!transitionFinished)
485 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
489 if (navigationPages.Count == 0)
491 throw new InvalidOperationException("There is no page in Navigator.");
494 //TODO: The following transition codes will be replaced with view transition.
495 InitializeAnimation();
499 if (navigationPages.Count == 1)
503 //Invoke Popped event
504 Popped?.Invoke(this, new PoppedEventArgs() { Page = curTop });
509 var newTop = navigationPages[navigationPages.Count - 2];
512 newTop.InvokeAppearing();
513 curTop.InvokeDisappearing();
514 curTop.SaveKeyFocus();
516 if (curTop is DialogPage == false)
518 curAnimation = new Animation(DefaultTransitionDuration);
519 curAnimation.AnimateTo(curTop, "PositionX", SizeWidth, 0, DefaultTransitionDuration);
520 curAnimation.EndAction = Animation.EndActions.StopFinal;
521 curAnimation.Finished += (object sender, EventArgs e) =>
523 //Removes the current top page after transition is finished.
526 curTop.PositionX = 0.0f;
529 curTop.InvokeDisappeared();
531 //Invoke Popped event
532 Popped?.Invoke(this, new PoppedEventArgs() { Page = curTop });
536 newTop.SetVisible(true);
537 // Set Content visible because it was hidden by HideContentOfPage.
538 (newTop as ContentPage)?.Content?.SetVisible(true);
540 newAnimation = new Animation(DefaultTransitionDuration);
541 newAnimation.AnimateTo(newTop, "PositionX", 0.0f, 0, DefaultTransitionDuration);
542 newAnimation.EndAction = Animation.EndActions.StopFinal;
543 newAnimation.Finished += (object sender, EventArgs e) =>
545 // Need to update Content of the new page
546 ShowContentOfPage(newTop);
549 newTop.InvokeAppeared();
551 newTop.RestoreKeyFocus();
558 newTop.RestoreKeyFocus();
565 /// Returns the page of the given index in Navigator.
566 /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
567 /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
569 /// <param name="index">The index of a page in Navigator.</param>
570 /// <returns>The page of the given index in Navigator.</returns>
571 /// <exception cref="ArgumentOutOfRangeException">Thrown when the argument index is less than 0, or greater than the number of pages.</exception>
572 public Page GetPage(int index)
574 if ((index < 0) || (index > navigationPages.Count))
576 throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than or equal to the number of pages.");
579 return navigationPages[index];
583 /// Returns the current index of the given page in Navigator.
584 /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
585 /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
587 /// <param name="page">The page in Navigator.</param>
588 /// <returns>The index of the given page in Navigator. If the given page is not in the Navigator, then -1 is returned.</returns>
589 /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
590 /// <since_tizen> 9 </since_tizen>
591 public int IndexOf(Page page)
595 throw new ArgumentNullException(nameof(page), "page should not be null.");
598 for (int i = 0; i < navigationPages.Count; i++)
600 if (navigationPages[i] == page)
610 /// Inserts a page at the specified index of Navigator.
611 /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
612 /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
613 /// To find the current index of a page in Navigator, please use IndexOf(page).
614 /// If the page is already in Navigator, then it is not inserted.
616 /// <param name="index">The index of a page in Navigator where the page will be inserted.</param>
617 /// <param name="page">The page to insert to Navigator.</param>
618 /// <exception cref="ArgumentOutOfRangeException">Thrown when the argument index is less than 0, or greater than the number of pages.</exception>
619 /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
620 /// <since_tizen> 9 </since_tizen>
621 public void Insert(int index, Page page)
623 if ((index < 0) || (index > navigationPages.Count))
625 throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than or equal to the number of pages.");
630 throw new ArgumentNullException(nameof(page), "page should not be null.");
633 //Duplicate page is not pushed.
634 if (navigationPages.Contains(page)) return;
636 //TODO: The following transition codes will be replaced with view transition.
637 InitializeAnimation();
639 ShowContentOfPage(page);
641 if (index == PageCount)
643 page.SetVisible(true);
647 page.SetVisible(false);
650 navigationPages.Insert(index, page);
652 page.SiblingOrder = index;
653 page.Navigator = this;
654 if (index == PageCount - 1)
658 NotifyAccessibilityStatesChangeOfPages(navigationPages[PageCount - 2], page);
662 NotifyAccessibilityStatesChangeOfPages(null, page);
668 /// Inserts a page to Navigator before an existing page.
669 /// If the page is already in Navigator, then it is not inserted.
671 /// <param name="before">The existing page, before which a page will be inserted.</param>
672 /// <param name="page">The page to insert to Navigator.</param>
673 /// <exception cref="ArgumentNullException">Thrown when the argument before is null.</exception>
674 /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
675 /// <exception cref="ArgumentException">Thrown when the argument before does not exist in Navigator.</exception>
676 /// <since_tizen> 9 </since_tizen>
677 public void InsertBefore(Page before, Page page)
681 throw new ArgumentNullException(nameof(before), "before should not be null.");
686 throw new ArgumentNullException(nameof(page), "page should not be null.");
689 //Find the index of before page.
690 int beforeIndex = navigationPages.FindIndex(x => x == before);
692 //before does not exist in Navigator.
693 if (beforeIndex == -1)
695 throw new ArgumentException("before does not exist in Navigator.", nameof(before));
698 Insert(beforeIndex, page);
702 /// Removes a page from Navigator.
704 /// <param name="page">The page to remove from Navigator.</param>
705 /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
706 /// <since_tizen> 9 </since_tizen>
707 public void Remove(Page page)
711 throw new ArgumentNullException(nameof(page), "page should not be null.");
714 //TODO: The following transition codes will be replaced with view transition.
715 InitializeAnimation();
717 HideContentOfPage(page);
723 navigationPages[PageCount - 2].SetVisible(true);
724 NotifyAccessibilityStatesChangeOfPages(page, navigationPages[PageCount - 2]);
726 else if (PageCount == 1)
728 NotifyAccessibilityStatesChangeOfPages(page, null);
731 page.Navigator = null;
732 navigationPages.Remove(page);
737 /// Removes a page at the specified index of Navigator.
738 /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
739 /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
740 /// To find the current index of a page in Navigator, please use IndexOf(page).
742 /// <param name="index">The index of a page in Navigator where the page will be removed.</param>
743 /// <exception cref="ArgumentOutOfRangeException">Thrown when the index is less than 0, or greater than or equal to the number of pages.</exception>
744 /// <since_tizen> 9 </since_tizen>
745 public void RemoveAt(int index)
747 if ((index < 0) || (index >= navigationPages.Count))
749 throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than the number of pages.");
752 Remove(navigationPages[index]);
756 /// Returns the page at the top of Navigator.
758 /// <returns>The page at the top of Navigator.</returns>
759 /// <since_tizen> 9 </since_tizen>
762 if (navigationPages.Count == 0) return null;
764 return navigationPages[navigationPages.Count - 1];
768 /// Gets or sets if Navigator proceeds back navigation when back button or back key is pressed and released.
769 /// Back navigation pops the peek page if Navigator has more than one page.
770 /// If Navigator has only one page, then the current program is exited.
772 [EditorBrowsable(EditorBrowsableState.Never)]
773 public bool EnableBackNavigation
777 return (bool)GetValue(EnableBackNavigationProperty);
781 SetValue(EnableBackNavigationProperty, value);
782 NotifyPropertyChanged();
786 private bool InternalEnableBackNavigation
790 enableBackNavigation = value;
794 return enableBackNavigation;
799 /// Disposes Navigator and all children on it.
801 /// <param name="type">Dispose type.</param>
802 [EditorBrowsable(EditorBrowsableState.Never)]
803 protected override void Dispose(DisposeTypes type)
810 if (type == DisposeTypes.Explicit)
812 foreach (Page page in navigationPages)
814 Utility.Dispose(page);
816 navigationPages.Clear();
820 if (navigatorWindow.TryGetValue(this, out window) == true)
822 navigatorWindow.Remove(this);
823 windowNavigator.Remove(window);
826 AddedToWindow -= OnAddedToWindow;
827 RemovedFromWindow -= OnRemovedFromWindow;
834 /// Returns the default navigator of the given window.
836 /// <returns>The default navigator of the given window.</returns>
837 /// <exception cref="ArgumentNullException">Thrown when the argument window is null.</exception>
838 /// <since_tizen> 9 </since_tizen>
839 public static Navigator GetDefaultNavigator(Window window)
843 throw new ArgumentNullException(nameof(window), "window should not be null.");
846 if (windowNavigator.ContainsKey(window) == true)
848 return windowNavigator[window];
851 var defaultNavigator = new Navigator();
852 defaultNavigator.WidthResizePolicy = ResizePolicyType.FillToParent;
853 defaultNavigator.HeightResizePolicy = ResizePolicyType.FillToParent;
854 window.Add(defaultNavigator);
855 windowNavigator.Add(window, defaultNavigator);
856 navigatorWindow.Add(defaultNavigator, window);
858 return defaultNavigator;
862 /// Sets the default navigator of the given window.
863 /// SetDefaultNavigator does not remove the previous default navigator from the window.
864 /// Therefore, if a user does not want to show the previous default navigator, then it should be removed from the window manually.
866 /// <exception cref="ArgumentNullException">Thrown when the argument window is null.</exception>
867 /// <exception cref="ArgumentNullException">Thrown when the argument newNavigator is null.</exception>
868 [EditorBrowsable(EditorBrowsableState.Never)]
869 public static void SetDefaultNavigator(Window window, Navigator newNavigator)
873 throw new ArgumentNullException(nameof(window), "window should not be null.");
876 if (newNavigator == null)
878 throw new ArgumentNullException(nameof(newNavigator), "newNavigator should not be null.");
881 if (windowNavigator.ContainsKey(window) == true)
883 var oldNavigator = windowNavigator[window];
884 if (oldNavigator == newNavigator)
889 navigatorWindow.Remove(oldNavigator);
890 windowNavigator.Remove(window);
893 window.Add(newNavigator);
894 windowNavigator.Add(window, newNavigator);
895 navigatorWindow.Add(newNavigator, window);
899 /// Called when the back navigation is started.
900 /// Back navigation pops the peek page if Navigator has more than one page.
901 /// If Navigator has only one page, then the current program is exited.
903 /// <param name="eventArgs">The back navigation information.</param>
904 [EditorBrowsable(EditorBrowsableState.Never)]
905 protected virtual void OnBackNavigation(BackNavigationEventArgs eventArgs)
909 if (Peek().EnableBackNavigation)
911 Peek().NavigateBack();
916 NUIApplication.Current?.Exit();
921 /// Called when the back navigation is required outside Navigator.
923 internal void NavigateBack()
925 OnBackNavigation(new BackNavigationEventArgs());
929 /// Create Transitions between currentTopPage and newTopPage
931 /// <param name="currentTopPage">The top page of Navigator.</param>
932 /// <param name="newTopPage">The new top page after transition.</param>
933 /// <param name="pushTransition">True if this transition is for push new page</param>
934 private TransitionSet CreateTransitions(Page currentTopPage, Page newTopPage, bool pushTransition)
936 currentTopPage.SetVisible(true);
937 // Set Content visible because it was hidden by HideContentOfPage.
938 (currentTopPage as ContentPage)?.Content?.SetVisible(true);
940 newTopPage.SetVisible(true);
941 // Set Content visible because it was hidden by HideContentOfPage.
942 (newTopPage as ContentPage)?.Content?.SetVisible(true);
944 List<View> taggedViewsInNewTopPage = new List<View>();
945 RetrieveTaggedViews(taggedViewsInNewTopPage, newTopPage, true);
946 List<View> taggedViewsInCurrentTopPage = new List<View>();
947 RetrieveTaggedViews(taggedViewsInCurrentTopPage, currentTopPage, true);
949 List<KeyValuePair<View, View>> sameTaggedViewPair = new List<KeyValuePair<View, View>>();
950 foreach (View currentTopPageView in taggedViewsInCurrentTopPage)
952 bool findPair = false;
953 foreach (View newTopPageView in taggedViewsInNewTopPage)
955 if ((currentTopPageView.TransitionOptions != null) && (newTopPageView.TransitionOptions != null) &&
956 currentTopPageView.TransitionOptions?.TransitionTag == newTopPageView.TransitionOptions?.TransitionTag)
958 sameTaggedViewPair.Add(new KeyValuePair<View, View>(currentTopPageView, newTopPageView));
965 taggedViewsInNewTopPage.Remove(sameTaggedViewPair[sameTaggedViewPair.Count - 1].Value);
968 foreach (KeyValuePair<View, View> pair in sameTaggedViewPair)
970 taggedViewsInCurrentTopPage.Remove(pair.Key);
973 TransitionSet newTransitionSet = new TransitionSet();
974 foreach (KeyValuePair<View, View> pair in sameTaggedViewPair)
976 TransitionItem pairTransition = transition.CreateTransition(pair.Key, pair.Value, pushTransition);
977 if (pair.Value.TransitionOptions?.TransitionWithChild ?? false)
979 pairTransition.TransitionWithChild = true;
981 newTransitionSet.AddTransition(pairTransition);
984 newTransitionSet.Finished += (object sender, EventArgs e) =>
986 if (newTopPage.Layout != null)
988 newTopPage.Layout.RequestLayout();
990 if (currentTopPage.Layout != null)
992 currentTopPage.Layout.RequestLayout();
994 transitionFinished = true;
995 InvokeTransitionFinished();
996 transitionSet.Dispose();
999 if (!pushTransition || newTopPage is DialogPage == false)
1001 View transitionView = currentTopPage;
1002 if (currentTopPage is ContentPage)
1004 transitionView = (currentTopPage as ContentPage).Content;
1006 else if (currentTopPage is DialogPage)
1008 transitionView = (currentTopPage as DialogPage).Content;
1011 if (currentTopPage.DisappearingTransition != null && transitionView != null)
1013 TransitionItemBase disappearingTransition = currentTopPage.DisappearingTransition.CreateTransition(transitionView, false);
1014 disappearingTransition.TransitionWithChild = true;
1015 newTransitionSet.AddTransition(disappearingTransition);
1019 currentTopPage.SetVisible(false);
1022 if (pushTransition || currentTopPage is DialogPage == false)
1024 View transitionView = newTopPage;
1025 if (newTopPage is ContentPage)
1027 transitionView = (newTopPage as ContentPage).Content;
1029 else if (newTopPage is DialogPage)
1031 transitionView = (newTopPage as DialogPage).Content;
1034 if (newTopPage.AppearingTransition != null && transitionView != null)
1036 TransitionItemBase appearingTransition = newTopPage.AppearingTransition.CreateTransition(transitionView, true);
1037 appearingTransition.TransitionWithChild = true;
1038 newTransitionSet.AddTransition(appearingTransition);
1042 newTransitionSet.Play();
1044 return newTransitionSet;
1048 /// Retrieve Tagged Views in the view tree.
1050 /// <param name="taggedViews">Returned tagged view list..</param>
1051 /// <param name="view">Root View to get tagged child View.</param>
1052 /// <param name="isRoot">Flag to check current View is page or not</param>
1053 private void RetrieveTaggedViews(List<View> taggedViews, View view, bool isRoot)
1055 if (!isRoot && view.TransitionOptions != null)
1057 if (!string.IsNullOrEmpty(view.TransitionOptions?.TransitionTag))
1059 taggedViews.Add((view as View));
1060 if (view.TransitionOptions.TransitionWithChild)
1068 foreach (View child in view.Children)
1070 RetrieveTaggedViews(taggedViews, child, false);
1075 /// Notify accessibility states change of pages.
1077 /// <param name="disappearedPage">Disappeared page</param>
1078 /// <param name="appearedPage">Appeared page</param>
1079 private void NotifyAccessibilityStatesChangeOfPages(Page disappearedPage, Page appearedPage)
1081 if (disappearedPage != null)
1083 disappearedPage.UnregisterDefaultLabel();
1084 //We can call disappearedPage.NotifyAccessibilityStatesChange
1085 //To reduce accessibility events, we are using currently highlighted view instead
1086 View curHighlightedView = Accessibility.Accessibility.GetCurrentlyHighlightedView();
1087 if (curHighlightedView != null)
1089 curHighlightedView.NotifyAccessibilityStatesChange(new AccessibilityStates(AccessibilityState.Visible, AccessibilityState.Showing), AccessibilityStatesNotifyMode.Single);
1093 if (appearedPage != null)
1095 appearedPage.RegisterDefaultLabel();
1096 appearedPage.NotifyAccessibilityStatesChange(new AccessibilityStates(AccessibilityState.Visible, AccessibilityState.Showing), AccessibilityStatesNotifyMode.Single);
1100 internal void InvokeTransitionFinished()
1102 TransitionFinished?.Invoke(this, new EventArgs());
1105 //TODO: The following transition codes will be replaced with view transition.
1106 private void InitializeAnimation()
1108 bool isCurAnimPlaying = false;
1109 bool isNewAnimPlaying = false;
1111 if (curAnimation != null)
1113 if (curAnimation.State == Animation.States.Playing)
1115 isCurAnimPlaying = true;
1116 curAnimation.Stop();
1120 if (newAnimation != null)
1122 if (newAnimation.State == Animation.States.Playing)
1124 isNewAnimPlaying = true;
1125 newAnimation.Stop();
1129 if (isCurAnimPlaying)
1131 // To enable multiple Pop(), animation's Finished callback is required.
1132 // To call animation's Finished callback, FinishedSignal is emitted.
1133 curAnimation.FinishedSignal().Emit(curAnimation);
1134 curAnimation.Clear();
1136 // InitializeAnimation() can be called by FinishedSignal().Emit().
1137 // Not to cause null pointer dereference by calling InitializeAnimation() in InitializeAnimation(),
1138 // animation handle is assigned to be null only if the animation is playing.
1139 curAnimation = null;
1142 if (isNewAnimPlaying)
1144 // To enable multiple Pop(), animation's Finished callback is required.
1145 // To call animation's Finished callback, FinishedSignal is emitted.
1146 newAnimation.FinishedSignal().Emit(newAnimation);
1147 newAnimation.Clear();
1149 // InitializeAnimation() can be called by FinishedSignal().Emit().
1150 // Not to cause null pointer dereference by calling InitializeAnimation() in InitializeAnimation(),
1151 // animation handle is assigned to be null only if the animation is playing.
1152 newAnimation = null;
1156 // Show and Register Content of Page to Accessibility bridge
1157 private void ShowContentOfPage(Page page)
1159 View content = (page is DialogPage) ? (page as DialogPage)?.Content : (page as ContentPage)?.Content;
1160 if (content != null)
1162 content.Show(); // Calls RegisterDefaultLabel()
1166 // Hide and Remove Content of Page from Accessibility bridge
1167 private void HideContentOfPage(Page page)
1169 View content = (page is DialogPage) ? (page as DialogPage)?.Content : (page as ContentPage)?.Content;
1170 if (content != null)
1172 content.Hide(); // Calls UnregisterDefaultLabel()
1178 /// BackNavigationEventArgs is a class to record back navigation event arguments which will sent to user.
1180 [EditorBrowsable(EditorBrowsableState.Never)]
1181 public class BackNavigationEventArgs : EventArgs