X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2FTizen.NUI.Components%2FControls%2FNavigation%2FNavigator.cs;h=9d5dd839497849c16b31a82754f64bf141b3c216;hb=d7f6c7f1f78feb30e4973ef9d6eda5c892ed0cb5;hp=1be99280df253157c8ee242f7ea42b0ad69e86a3;hpb=e934e622a1509e649ed4a6fe840b89f74d26b990;p=platform%2Fcore%2Fcsapi%2Ftizenfx.git diff --git a/src/Tizen.NUI.Components/Controls/Navigation/Navigator.cs b/src/Tizen.NUI.Components/Controls/Navigation/Navigator.cs index 1be9928..9d5dd83 100755 --- a/src/Tizen.NUI.Components/Controls/Navigation/Navigator.cs +++ b/src/Tizen.NUI.Components/Controls/Navigation/Navigator.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; using Tizen.NUI.BaseComponents; +using Tizen.NUI.Binding; namespace Tizen.NUI.Components { @@ -73,7 +74,43 @@ namespace Tizen.NUI.Components /// 9 public class Navigator : Control { - private const int DefaultTransitionDuration = 500; + /// + /// TransitionProperty + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public static readonly BindableProperty TransitionProperty = BindableProperty.Create(nameof(Transition), typeof(Transition), typeof(Navigator), null, propertyChanged: (bindable, oldValue, newValue) => + { + var instance = (Navigator)bindable; + if (newValue != null) + { + instance.InternalTransition = newValue as Transition; + } + }, + defaultValueCreator: (bindable) => + { + var instance = (Navigator)bindable; + return instance.InternalTransition; + }); + + /// + /// EnableBackNavigationProperty + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public static readonly BindableProperty EnableBackNavigationProperty = BindableProperty.Create(nameof(EnableBackNavigation), typeof(bool), typeof(Navigator), default(bool), propertyChanged: (bindable, oldValue, newValue) => + { + var instance = (Navigator)bindable; + if (newValue != null) + { + instance.InternalEnableBackNavigation = (bool)newValue; + } + }, + defaultValueCreator: (bindable) => + { + var instance = (Navigator)bindable; + return instance.InternalEnableBackNavigation; + }); + + private const int DefaultTransitionDuration = 300; //This will be replaced with view transition class instance. private Animation curAnimation = null; @@ -98,13 +135,76 @@ namespace Tizen.NUI.Components private List navigationPages = new List(); + private bool enableBackNavigation = true; + + private Window parentWindow; + + private void OnWindowKeyEvent(object sender, Window.KeyEventArgs e) + { + if (!EnableBackNavigation) + { + return; + } + + if ((e.Key.State == Key.StateType.Up) && ((e.Key.KeyPressedName == "Escape") || (e.Key.KeyPressedName == "BackSpace") || (e.Key.KeyPressedName == "XF86Back"))) + { + OnBackNavigation(new BackNavigationEventArgs()); + } + } + + private void OnAddedToWindow(object sender, EventArgs e) + { + parentWindow = Window.Get(this); + if (null != parentWindow) + { + parentWindow.KeyEvent += OnWindowKeyEvent; + } + } + + private void OnRemovedFromWindow(object sender, EventArgs e) + { + if (null != parentWindow) + { + parentWindow.KeyEvent -= OnWindowKeyEvent; + parentWindow = null; + } + } + + private void Initialize() + { + Layout = new AbsoluteLayout(); + + AddedToWindow += OnAddedToWindow; + RemovedFromWindow += OnRemovedFromWindow; + } + /// /// Creates a new instance of a Navigator. /// /// 9 public Navigator() : base() { - Layout = new AbsoluteLayout(); + Initialize(); + } + + /// + /// Creates a new instance of Navigator with style. + /// + /// Creates Navigator by special style defined in UX. + [EditorBrowsable(EditorBrowsableState.Never)] + public Navigator(string style) : base(style) + { + Initialize(); + } + + /// + /// Creates a new instance of a Navigator with style. + /// + /// A style applied to the newly created Navigator. + [EditorBrowsable(EditorBrowsableState.Never)] + public Navigator(ControlStyle style) : base(style) + { + Initialize(); } /// @@ -113,7 +213,7 @@ namespace Tizen.NUI.Components { base.OnInitialize(); - SetAccessibilityConstructor(Role.PageTabList); + AccessibilityRole = Role.PageTabList; } /// @@ -143,6 +243,18 @@ namespace Tizen.NUI.Components /// 9 public Transition Transition { + get + { + return GetValue(TransitionProperty) as Transition; + } + set + { + SetValue(TransitionProperty, value); + NotifyPropertyChanged(); + } + } + private Transition InternalTransition + { set { transition = value; @@ -191,18 +303,25 @@ namespace Tizen.NUI.Components //Invoke Page events page.InvokeAppearing(); topPage.InvokeDisappearing(); + topPage.SaveKeyFocus(); transitionSet = CreateTransitions(topPage, page, true); transitionSet.Finished += (object sender, EventArgs e) => { if (page is DialogPage == false) { - topPage.SetVisible(false); + topPage.SetVisible(false); } + // Need to update Content of the new page + ShowContentOfPage(page); + //Invoke Page events page.InvokeAppeared(); + page.RestoreKeyFocus(); + topPage.InvokeDisappeared(); + NotifyAccessibilityStatesChangeOfPages(topPage, page); }; transitionFinished = false; } @@ -242,6 +361,7 @@ namespace Tizen.NUI.Components //Invoke Page events newTopPage.InvokeAppearing(); topPage.InvokeDisappearing(); + topPage.SaveKeyFocus(); transitionSet = CreateTransitions(topPage, newTopPage, false); transitionSet.Finished += (object sender, EventArgs e) => @@ -249,8 +369,13 @@ namespace Tizen.NUI.Components Remove(topPage); topPage.SetVisible(true); + // Need to update Content of the new page + ShowContentOfPage(newTopPage); + //Invoke Page events newTopPage.InvokeAppeared(); + newTopPage.RestoreKeyFocus(); + topPage.InvokeDisappeared(); //Invoke Popped event @@ -284,6 +409,9 @@ namespace Tizen.NUI.Components //Duplicate page is not pushed. if (navigationPages.Contains(page)) return; + //TODO: The following transition codes will be replaced with view transition. + InitializeAnimation(); + var curTop = Peek(); if (!curTop) @@ -300,13 +428,12 @@ namespace Tizen.NUI.Components page.InvokeAppearing(); curTop.InvokeDisappearing(); - //TODO: The following transition codes will be replaced with view transition. - InitializeAnimation(); + curTop.SaveKeyFocus(); if (page is DialogPage == false) { - curAnimation = new Animation(1000); - curAnimation.AnimateTo(curTop, "Opacity", 1.0f, 0, 1000); + curAnimation = new Animation(DefaultTransitionDuration); + curAnimation.AnimateTo(curTop, "PositionX", 0.0f, 0, DefaultTransitionDuration); curAnimation.EndAction = Animation.EndActions.StopFinal; curAnimation.Finished += (object sender, EventArgs args) => { @@ -317,18 +444,32 @@ namespace Tizen.NUI.Components }; curAnimation.Play(); - page.Opacity = 0.0f; + page.PositionX = SizeWidth; page.SetVisible(true); - newAnimation = new Animation(1000); - newAnimation.AnimateTo(page, "Opacity", 1.0f, 0, 1000); + // Set Content visible because it was hidden by HideContentOfPage. + (page as ContentPage)?.Content?.SetVisible(true); + + newAnimation = new Animation(DefaultTransitionDuration); + newAnimation.AnimateTo(page, "PositionX", 0.0f, 0, DefaultTransitionDuration); newAnimation.EndAction = Animation.EndActions.StopFinal; newAnimation.Finished += (object sender, EventArgs e) => { + // Need to update Content of the new page + ShowContentOfPage(page); + //Invoke Page events page.InvokeAppeared(); + NotifyAccessibilityStatesChangeOfPages(curTop, page); + + page.RestoreKeyFocus(); }; newAnimation.Play(); } + else + { + ShowContentOfPage(page); + page.RestoreKeyFocus(); + } } /// @@ -350,6 +491,9 @@ namespace Tizen.NUI.Components throw new InvalidOperationException("There is no page in Navigator."); } + //TODO: The following transition codes will be replaced with view transition. + InitializeAnimation(); + var curTop = Peek(); if (navigationPages.Count == 1) @@ -367,20 +511,19 @@ namespace Tizen.NUI.Components //Invoke Page events newTop.InvokeAppearing(); curTop.InvokeDisappearing(); - - //TODO: The following transition codes will be replaced with view transition. - InitializeAnimation(); + curTop.SaveKeyFocus(); if (curTop is DialogPage == false) { - curAnimation = new Animation(1000); - curAnimation.AnimateTo(curTop, "Opacity", 0.0f, 0, 1000); + curAnimation = new Animation(DefaultTransitionDuration); + curAnimation.AnimateTo(curTop, "PositionX", SizeWidth, 0, DefaultTransitionDuration); curAnimation.EndAction = Animation.EndActions.StopFinal; curAnimation.Finished += (object sender, EventArgs e) => { //Removes the current top page after transition is finished. Remove(curTop); - curTop.Opacity = 1.0f; + + curTop.PositionX = 0.0f; //Invoke Page events curTop.InvokeDisappeared(); @@ -390,21 +533,29 @@ namespace Tizen.NUI.Components }; curAnimation.Play(); - newTop.Opacity = 1.0f; newTop.SetVisible(true); - newAnimation = new Animation(1000); - newAnimation.AnimateTo(newTop, "Opacity", 1.0f, 0, 1000); + // Set Content visible because it was hidden by HideContentOfPage. + (newTop as ContentPage)?.Content?.SetVisible(true); + + newAnimation = new Animation(DefaultTransitionDuration); + newAnimation.AnimateTo(newTop, "PositionX", 0.0f, 0, DefaultTransitionDuration); newAnimation.EndAction = Animation.EndActions.StopFinal; newAnimation.Finished += (object sender, EventArgs e) => { + // Need to update Content of the new page + ShowContentOfPage(newTop); + //Invoke Page events newTop.InvokeAppeared(); + + newTop.RestoreKeyFocus(); }; newAnimation.Play(); } else { Remove(curTop); + newTop.RestoreKeyFocus(); } return curTop; @@ -485,20 +636,32 @@ namespace Tizen.NUI.Components //TODO: The following transition codes will be replaced with view transition. InitializeAnimation(); + ShowContentOfPage(page); + if (index == PageCount) { - page.Opacity = 1.0f; page.SetVisible(true); } else { page.SetVisible(false); - page.Opacity = 0.0f; } navigationPages.Insert(index, page); Add(page); + page.SiblingOrder = index; page.Navigator = this; + if (index == PageCount - 1) + { + if (PageCount > 1) + { + NotifyAccessibilityStatesChangeOfPages(navigationPages[PageCount - 2], page); + } + else + { + NotifyAccessibilityStatesChangeOfPages(null, page); + } + } } /// @@ -551,12 +714,20 @@ namespace Tizen.NUI.Components //TODO: The following transition codes will be replaced with view transition. InitializeAnimation(); - if ((page == Peek()) && (PageCount >= 2)) + HideContentOfPage(page); + + if (page == Peek()) { - navigationPages[PageCount - 2].Opacity = 1.0f; - navigationPages[PageCount - 2].SetVisible(true); + if (PageCount >= 2) + { + navigationPages[PageCount - 2].SetVisible(true); + NotifyAccessibilityStatesChangeOfPages(page, navigationPages[PageCount - 2]); + } + else if (PageCount == 1) + { + NotifyAccessibilityStatesChangeOfPages(page, null); + } } - page.Navigator = null; navigationPages.Remove(page); base.Remove(page); @@ -594,6 +765,37 @@ namespace Tizen.NUI.Components } /// + /// Gets or sets if Navigator proceeds back navigation when back button or back key is pressed and released. + /// Back navigation pops the peek page if Navigator has more than one page. + /// If Navigator has only one page, then the current program is exited. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public bool EnableBackNavigation + { + get + { + return (bool)GetValue(EnableBackNavigationProperty); + } + set + { + SetValue(EnableBackNavigationProperty, value); + NotifyPropertyChanged(); + } + } + + private bool InternalEnableBackNavigation + { + set + { + enableBackNavigation = value; + } + get + { + return enableBackNavigation; + } + } + + /// /// Disposes Navigator and all children on it. /// /// Dispose type. @@ -620,6 +822,9 @@ namespace Tizen.NUI.Components navigatorWindow.Remove(this); windowNavigator.Remove(window); } + + AddedToWindow -= OnAddedToWindow; + RemovedFromWindow -= OnRemovedFromWindow; } base.Dispose(type); @@ -654,6 +859,73 @@ namespace Tizen.NUI.Components } /// + /// Sets the default navigator of the given window. + /// SetDefaultNavigator does not remove the previous default navigator from the window. + /// Therefore, if a user does not want to show the previous default navigator, then it should be removed from the window manually. + /// + /// Thrown when the argument window is null. + /// Thrown when the argument newNavigator is null. + [EditorBrowsable(EditorBrowsableState.Never)] + public static void SetDefaultNavigator(Window window, Navigator newNavigator) + { + if (window == null) + { + throw new ArgumentNullException(nameof(window), "window should not be null."); + } + + if (newNavigator == null) + { + throw new ArgumentNullException(nameof(newNavigator), "newNavigator should not be null."); + } + + if (windowNavigator.ContainsKey(window) == true) + { + var oldNavigator = windowNavigator[window]; + if (oldNavigator == newNavigator) + { + return; + } + + navigatorWindow.Remove(oldNavigator); + windowNavigator.Remove(window); + } + + window.Add(newNavigator); + windowNavigator.Add(window, newNavigator); + navigatorWindow.Add(newNavigator, window); + } + + /// + /// Called when the back navigation is started. + /// Back navigation pops the peek page if Navigator has more than one page. + /// If Navigator has only one page, then the current program is exited. + /// + /// The back navigation information. + [EditorBrowsable(EditorBrowsableState.Never)] + protected virtual void OnBackNavigation(BackNavigationEventArgs eventArgs) + { + if (PageCount >= 1) + { + if (Peek().EnableBackNavigation) + { + Peek().NavigateBack(); + } + } + else + { + NUIApplication.Current?.Exit(); + } + } + + /// + /// Called when the back navigation is required outside Navigator. + /// + internal void NavigateBack() + { + OnBackNavigation(new BackNavigationEventArgs()); + } + + /// /// Create Transitions between currentTopPage and newTopPage /// /// The top page of Navigator. @@ -662,7 +934,12 @@ namespace Tizen.NUI.Components private TransitionSet CreateTransitions(Page currentTopPage, Page newTopPage, bool pushTransition) { currentTopPage.SetVisible(true); + // Set Content visible because it was hidden by HideContentOfPage. + (currentTopPage as ContentPage)?.Content?.SetVisible(true); + newTopPage.SetVisible(true); + // Set Content visible because it was hidden by HideContentOfPage. + (newTopPage as ContentPage)?.Content?.SetVisible(true); List taggedViewsInNewTopPage = new List(); RetrieveTaggedViews(taggedViewsInNewTopPage, newTopPage, true); @@ -670,12 +947,12 @@ namespace Tizen.NUI.Components RetrieveTaggedViews(taggedViewsInCurrentTopPage, currentTopPage, true); List> sameTaggedViewPair = new List>(); - foreach(View currentTopPageView in taggedViewsInCurrentTopPage) + foreach (View currentTopPageView in taggedViewsInCurrentTopPage) { bool findPair = false; - foreach(View newTopPageView in taggedViewsInNewTopPage) + foreach (View newTopPageView in taggedViewsInNewTopPage) { - if((currentTopPageView.TransitionOptions != null) && (newTopPageView.TransitionOptions != null) && + if ((currentTopPageView.TransitionOptions != null) && (newTopPageView.TransitionOptions != null) && currentTopPageView.TransitionOptions?.TransitionTag == newTopPageView.TransitionOptions?.TransitionTag) { sameTaggedViewPair.Add(new KeyValuePair(currentTopPageView, newTopPageView)); @@ -683,21 +960,21 @@ namespace Tizen.NUI.Components break; } } - if(findPair) + if (findPair) { taggedViewsInNewTopPage.Remove(sameTaggedViewPair[sameTaggedViewPair.Count - 1].Value); } } - foreach(KeyValuePair pair in sameTaggedViewPair) + foreach (KeyValuePair pair in sameTaggedViewPair) { taggedViewsInCurrentTopPage.Remove(pair.Key); } TransitionSet newTransitionSet = new TransitionSet(); - foreach(KeyValuePair pair in sameTaggedViewPair) + foreach (KeyValuePair pair in sameTaggedViewPair) { TransitionItem pairTransition = transition.CreateTransition(pair.Key, pair.Value, pushTransition); - if(pair.Value.TransitionOptions?.TransitionWithChild ?? false) + if (pair.Value.TransitionOptions?.TransitionWithChild ?? false) { pairTransition.TransitionWithChild = true; } @@ -706,23 +983,31 @@ namespace Tizen.NUI.Components newTransitionSet.Finished += (object sender, EventArgs e) => { - if(newTopPage.Layout != null) + if (newTopPage.Layout != null) { newTopPage.Layout.RequestLayout(); } - if(currentTopPage.Layout != null) + if (currentTopPage.Layout != null) { currentTopPage.Layout.RequestLayout(); } transitionFinished = true; InvokeTransitionFinished(); transitionSet.Dispose(); - currentTopPage.Opacity = 1.0f; }; if (!pushTransition || newTopPage is DialogPage == false) { - View transitionView = (currentTopPage is ContentPage) ? (currentTopPage as ContentPage).Content : (currentTopPage as DialogPage).Content; + View transitionView = currentTopPage; + if (currentTopPage is ContentPage) + { + transitionView = (currentTopPage as ContentPage).Content; + } + else if (currentTopPage is DialogPage) + { + transitionView = (currentTopPage as DialogPage).Content; + } + if (currentTopPage.DisappearingTransition != null && transitionView != null) { TransitionItemBase disappearingTransition = currentTopPage.DisappearingTransition.CreateTransition(transitionView, false); @@ -736,7 +1021,16 @@ namespace Tizen.NUI.Components } if (pushTransition || currentTopPage is DialogPage == false) { - View transitionView = (newTopPage is ContentPage) ? (newTopPage as ContentPage).Content : (newTopPage as DialogPage).Content; + View transitionView = newTopPage; + if (newTopPage is ContentPage) + { + transitionView = (newTopPage as ContentPage).Content; + } + else if (newTopPage is DialogPage) + { + transitionView = (newTopPage as DialogPage).Content; + } + if (newTopPage.AppearingTransition != null && transitionView != null) { TransitionItemBase appearingTransition = newTopPage.AppearingTransition.CreateTransition(transitionView, true); @@ -777,6 +1071,32 @@ namespace Tizen.NUI.Components } } + /// + /// Notify accessibility states change of pages. + /// + /// Disappeared page + /// Appeared page + private void NotifyAccessibilityStatesChangeOfPages(Page disappearedPage, Page appearedPage) + { + if (disappearedPage != null) + { + disappearedPage.UnregisterDefaultLabel(); + //We can call disappearedPage.NotifyAccessibilityStatesChange + //To reduce accessibility events, we are using currently highlighted view instead + View curHighlightedView = Accessibility.Accessibility.GetCurrentlyHighlightedView(); + if (curHighlightedView != null) + { + curHighlightedView.NotifyAccessibilityStatesChange(new AccessibilityStates(AccessibilityState.Visible, AccessibilityState.Showing), AccessibilityStatesNotifyMode.Single); + } + } + + if (appearedPage != null) + { + appearedPage.RegisterDefaultLabel(); + appearedPage.NotifyAccessibilityStatesChange(new AccessibilityStates(AccessibilityState.Visible, AccessibilityState.Showing), AccessibilityStatesNotifyMode.Single); + } + } + internal void InvokeTransitionFinished() { TransitionFinished?.Invoke(this, new EventArgs()); @@ -785,19 +1105,80 @@ namespace Tizen.NUI.Components //TODO: The following transition codes will be replaced with view transition. private void InitializeAnimation() { + bool isCurAnimPlaying = false; + bool isNewAnimPlaying = false; + if (curAnimation != null) { - curAnimation.Stop(); + if (curAnimation.State == Animation.States.Playing) + { + isCurAnimPlaying = true; + curAnimation.Stop(); + } + } + + if (newAnimation != null) + { + if (newAnimation.State == Animation.States.Playing) + { + isNewAnimPlaying = true; + newAnimation.Stop(); + } + } + + if (isCurAnimPlaying) + { + // To enable multiple Pop(), animation's Finished callback is required. + // To call animation's Finished callback, FinishedSignal is emitted. + curAnimation.FinishedSignal().Emit(curAnimation); curAnimation.Clear(); + + // InitializeAnimation() can be called by FinishedSignal().Emit(). + // Not to cause null pointer dereference by calling InitializeAnimation() in InitializeAnimation(), + // animation handle is assigned to be null only if the animation is playing. curAnimation = null; } - if (newAnimation != null) + if (isNewAnimPlaying) { - newAnimation.Stop(); + // To enable multiple Pop(), animation's Finished callback is required. + // To call animation's Finished callback, FinishedSignal is emitted. + newAnimation.FinishedSignal().Emit(newAnimation); newAnimation.Clear(); + + // InitializeAnimation() can be called by FinishedSignal().Emit(). + // Not to cause null pointer dereference by calling InitializeAnimation() in InitializeAnimation(), + // animation handle is assigned to be null only if the animation is playing. newAnimation = null; } } + + // Show and Register Content of Page to Accessibility bridge + private void ShowContentOfPage(Page page) + { + View content = (page is DialogPage) ? (page as DialogPage)?.Content : (page as ContentPage)?.Content; + if (content != null) + { + content.Show(); // Calls RegisterDefaultLabel() + } + } + + // Hide and Remove Content of Page from Accessibility bridge + private void HideContentOfPage(Page page) + { + View content = (page is DialogPage) ? (page as DialogPage)?.Content : (page as ContentPage)?.Content; + if (content != null) + { + content.Hide(); // Calls UnregisterDefaultLabel() + } + } + } + + /// + /// BackNavigationEventArgs is a class to record back navigation event arguments which will sent to user. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public class BackNavigationEventArgs : EventArgs + { } }