/* * Copyright(c) 2020 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using Tizen.NUI.BaseComponents; using Tizen.NUI.Binding; namespace Tizen.NUI.Components { /// /// The Navigator is a class which navigates pages with stack methods such /// as Push and Pop. /// [EditorBrowsable(EditorBrowsableState.Never)] public class Navigator : Control { //This will be replaced with view transition class instance. private Animation _curAnimation = null; //This will be replaced with view transition class instance. private Animation _newAnimation = null; //TODO: Needs to consider how to remove disposed window from dictionary. //Two dictionaries are required to remove disposed navigator from dictionary. private static Dictionary windowNavigator = new Dictionary(); private static Dictionary navigatorWindow = new Dictionary(); /// /// Creates a new instance of a Navigator. /// [EditorBrowsable(EditorBrowsableState.Never)] public Navigator() : base() { } /// /// List of pages of Navigator. /// [EditorBrowsable(EditorBrowsableState.Never)] public List NavigationPages { get; } = new List(); /// /// Pushes a page to Navigator. /// If the page is already in Navigator, then it is not pushed. /// /// The page to push to Navigator. /// Thrown when the argument page is null. [EditorBrowsable(EditorBrowsableState.Never)] public void Push(Page page) { if (page == null) { throw new ArgumentNullException(nameof(page), "page should not be null."); } //Duplicate page is not pushed. if (NavigationPages.Contains(page)) return; var curTop = Peek(); if (!curTop) { Insert(0, page); return; } NavigationPages.Add(page); Add(page); //Invoke Page events page.InvokeAppearing(); curTop.InvokeDisappearing(); //TODO: The following transition codes will be replaced with view transition. if (_curAnimation) { _curAnimation.Stop(); _curAnimation.Clear(); } if (_newAnimation) { _newAnimation.Stop(); _newAnimation.Clear(); } _curAnimation = new Animation(1000); using (var scaleVec = new Vector3(0.0f, 0.0f, 1.0f)) { _curAnimation.AnimateTo(curTop, "Scale", scaleVec, 0, 1000); } _curAnimation.AnimateTo(curTop, "Opacity", 0.0f, 0, 1000); _curAnimation.EndAction = Animation.EndActions.Discard; _curAnimation.Play(); using (var scaleVec = new Vector3(0.0f, 0.0f, 1.0f)) { using (var scaleProp = new Tizen.NUI.PropertyValue(scaleVec)) { Tizen.NUI.Object.SetProperty(page.SwigCPtr, Page.Property.SCALE, scaleProp); } } using (var scaleProp = new Tizen.NUI.PropertyValue(0.0f)) { Tizen.NUI.Object.SetProperty(page.SwigCPtr, Page.Property.OPACITY, scaleProp); } _newAnimation = new Animation(1000); using (var scaleVec = new Vector3(1.0f, 1.0f, 1.0f)) { _newAnimation.AnimateTo(page, "Scale", scaleVec, 0, 1000); } _newAnimation.AnimateTo(page, "Opacity", 1.0f, 0, 1000); _newAnimation.Play(); } /// /// Pops the top page from Navigator. /// /// The popped page. /// Thrown when there is no page in Navigator. [EditorBrowsable(EditorBrowsableState.Never)] public Page Pop() { if (NavigationPages.Count == 0) { throw new InvalidOperationException("There is no page in Navigator."); } var curTop = Peek(); if (NavigationPages.Count == 1) { Remove(curTop); return curTop; } var newTop = NavigationPages[NavigationPages.Count - 2]; //Invoke Page events newTop.InvokeAppearing(); curTop.InvokeDisappearing(); //TODO: The following transition codes will be replaced with view transition. if (_curAnimation) { _curAnimation.Stop(); _curAnimation.Clear(); } if (_newAnimation) { _newAnimation.Stop(); _newAnimation.Clear(); } _curAnimation = new Animation(1000); using (var scaleVec = new Vector3(0.0f, 0.0f, 1.0f)) { _curAnimation.AnimateTo(curTop, "Scale", scaleVec, 0, 1000); } _curAnimation.AnimateTo(curTop, "Opacity", 0.0f, 0, 1000); _curAnimation.Play(); _curAnimation.Finished += (object sender, EventArgs e) => { //Removes the current top page after transition is finished. Remove(curTop); }; using (var scaleVec = new Vector3(0.0f, 0.0f, 1.0f)) { using (var scaleProp = new Tizen.NUI.PropertyValue(scaleVec)) { Tizen.NUI.Object.SetProperty(newTop.SwigCPtr, Page.Property.SCALE, scaleProp); } } using (var opacityProp = new Tizen.NUI.PropertyValue(0.0f)) { Tizen.NUI.Object.SetProperty(newTop.SwigCPtr, Page.Property.OPACITY, opacityProp); } _newAnimation = new Animation(1000); using (var scaleVec = new Vector3(1.0f, 1.0f, 1.0f)) { _newAnimation.AnimateTo(newTop, "Scale", scaleVec, 0, 1000); } _newAnimation.AnimateTo(newTop, "Opacity", 1.0f, 0, 1000); _newAnimation.Play(); return curTop; } /// /// Inserts a page at the specified index of Navigator. /// If the page is already in Navigator, then it is not inserted. /// /// The index of Navigator where a page will be inserted. /// The page to insert to Navigator. /// Thrown when the argument index is less than 0, or greater than the number of pages. /// Thrown when the argument page is null. [EditorBrowsable(EditorBrowsableState.Never)] public void Insert(int index, Page page) { if ((index < 0) || (index > NavigationPages.Count)) { throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than or equal to the number of pages."); } if (page == null) { throw new ArgumentNullException(nameof(page), "page should not be null."); } //Duplicate page is not pushed. if (NavigationPages.Contains(page)) return; NavigationPages.Insert(index, page); Add(page); } /// /// Inserts a page to Navigator before an existing page. /// If the page is already in Navigator, then it is not inserted. /// /// The existing page, before which a page will be inserted. /// The page to insert to Navigator. /// Thrown when the argument before is null. /// Thrown when the argument page is null. /// Thrown when the argument before does not exist in Navigator. [EditorBrowsable(EditorBrowsableState.Never)] public void InsertBefore(Page before, Page page) { if (before == null) { throw new ArgumentNullException(nameof(before), "before should not be null."); } if (page == null) { throw new ArgumentNullException(nameof(page), "page should not be null."); } //Find the index of before page. int beforeIndex = NavigationPages.FindIndex(x => x == before); //before does not exist in Navigator. if (beforeIndex == -1) { throw new ArgumentException("before does not exist in Navigator.", nameof(before)); } Insert(beforeIndex, page); } /// /// Removes a page from Navigator. /// /// The page to remove from Navigator. /// Thrown when the argument page is null. [EditorBrowsable(EditorBrowsableState.Never)] public void Remove(Page page) { if (page == null) { throw new ArgumentNullException(nameof(page), "page should not be null."); } NavigationPages.Remove(page); base.Remove(page); } /// /// Removes a page at the specified index of Navigator. /// /// The index of Navigator where a page will be removed. /// Thrown when the index is less than 0, or greater than or equal to the number of pages. [EditorBrowsable(EditorBrowsableState.Never)] public void RemoveAt(int index) { if ((index < 0) || (index >= NavigationPages.Count)) { throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than the number of pages."); } Remove(NavigationPages[index]); } /// /// Returns the page at the top of Navigator. /// /// The page at the top of Navigator. [EditorBrowsable(EditorBrowsableState.Never)] public Page Peek() { if (NavigationPages.Count == 0) return null; return NavigationPages[NavigationPages.Count - 1]; } /// /// Disposes Navigator and all children on it. /// /// Dispose type. [EditorBrowsable(EditorBrowsableState.Never)] protected override void Dispose(DisposeTypes type) { if (disposed) { return; } if (type == DisposeTypes.Explicit) { foreach (Page page in NavigationPages) { Utility.Dispose(page); } NavigationPages.Clear(); Window window; if (navigatorWindow.TryGetValue(this, out window) == true) { navigatorWindow.Remove(this); windowNavigator.Remove(window); } } base.Dispose(type); } /// /// Returns the default navigator of the given window. /// /// The default navigator of the given window. /// Thrown when the argument window is null. [EditorBrowsable(EditorBrowsableState.Never)] public static Navigator GetDefaultNavigator(Window window) { if (window == null) { throw new ArgumentNullException(nameof(window), "window should not be null."); } if (windowNavigator.ContainsKey(window) == true) { return windowNavigator[window]; } var defaultNavigator = new Navigator(); defaultNavigator.WidthResizePolicy = ResizePolicyType.FillToParent; defaultNavigator.HeightResizePolicy = ResizePolicyType.FillToParent; window.Add(defaultNavigator); windowNavigator.Add(window, defaultNavigator); navigatorWindow.Add(defaultNavigator, window); return defaultNavigator; } /// /// Shows a dialog by pushing a page containing dialog to default navigator. /// /// The content of Dialog. [EditorBrowsable(EditorBrowsableState.Never)] [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "The pushed views are added to NavigationPages and are disposed in Navigator.Dispose().")] public static void ShowDialog(View content = null) { var window = NUIApplication.GetDefaultWindow(); var defaultNavigator = window.GetDefaultNavigator(); var dialog = new Dialog(content); SetDialogScrim(dialog); var dialogPage = new Page(dialog); defaultNavigator.Push(dialogPage); } /// /// Shows an alert dialog by pushing a page containing the alert dialog /// to default navigator. /// /// The title content of AlertDialog. /// The content of AlertDialog. /// The action content of AlertDialog. [EditorBrowsable(EditorBrowsableState.Never)] [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "The pushed views are added to NavigationPages and are disposed in Navigator.Dispose().")] public static void ShowAlertDialog(View titleContent, View content, View actionContent) { var window = NUIApplication.GetDefaultWindow(); var defaultNavigator = window.GetDefaultNavigator(); var dialog = new AlertDialog(titleContent, content, actionContent); SetDialogScrim(dialog); var dialogPage = new Page(dialog); defaultNavigator.Push(dialogPage); } /// /// Shows an alert dialog by pushing a page containing the alert dialog /// to default navigator. /// /// The title of AlertDialog. /// The message of AlertDialog. /// The positive button text in the action content of AlertDialog. /// The clicked callback of the positive button in the action content of AlertDialog. /// The negative button text in the action content of AlertDialog. /// The clicked callback of the negative button in the action content of AlertDialog. [EditorBrowsable(EditorBrowsableState.Never)] [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "The pushed views are added to NavigationPages and are disposed in Navigator.Dispose().")] public static void ShowAlertDialog(string title = null, string message = null, string positiveButtonText = null, EventHandler positiveButtonClickedHandler = null, string negativeButtonText = null, EventHandler negativeButtonClickedHandler = null) { var window = NUIApplication.GetDefaultWindow(); var defaultNavigator = window.GetDefaultNavigator(); var dialog = new AlertDialog(title, message, positiveButtonText, positiveButtonClickedHandler, negativeButtonText, negativeButtonClickedHandler); SetDialogScrim(dialog); var dialogPage = new Page(dialog); defaultNavigator.Push(dialogPage); } private static void SetDialogScrim(Dialog dialog) { if (dialog == null) { return; } var window = NUIApplication.GetDefaultWindow(); var defaultNavigator = window.GetDefaultNavigator(); var defaultScrim = dialog.Scrim; //Copies default scrim's GUI properties. var scrim = new VisualView(); scrim.BackgroundColor = defaultScrim.BackgroundColor; scrim.Size = defaultScrim.Size; scrim.TouchEvent += (object source, View.TouchEventArgs e) => { if (e.Touch.GetState(0) == PointStateType.Up) { defaultNavigator.Pop(); } return true; }; dialog.Scrim = scrim; } } } //namespace Tizen.NUI