Revert "[NUI] Rebase DevelNUI (#2507)" (#2508)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / Navigation / Navigator.cs
1 /*
2  * Copyright(c) 2020 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 using System;
19 using System.Collections.Generic;
20 using System.ComponentModel;
21 using System.Threading.Tasks;
22 using Tizen.NUI.BaseComponents;
23 using Tizen.NUI.Binding;
24
25 namespace Tizen.NUI.Components
26 {
27     /// <summary>
28     /// The Navigator is a class which navigates pages with stack methods such
29     /// as Push and Pop.
30     /// </summary>
31     [EditorBrowsable(EditorBrowsableState.Never)]
32     public class Navigator : Control
33     {
34         //This will be replaced with view transition class instance.
35         private Animation _curAnimation = null;
36
37         //This will be replaced with view transition class instance.
38         private Animation _newAnimation = null;
39
40         private static Dictionary<Window, Navigator> windowNavigator = new Dictionary<Window, Navigator>();
41
42         /// <summary>
43         /// Creates a new instance of a Navigator.
44         /// </summary>
45         [EditorBrowsable(EditorBrowsableState.Never)]
46         public Navigator() : base()
47         {
48         }
49
50         /// <summary>
51         /// List of pages of Navigator.
52         /// </summary>
53         [EditorBrowsable(EditorBrowsableState.Never)]
54         public List<Page> NavigationPages { get; } = new List<Page>();
55
56         /// <summary>
57         /// Pushes a page to Navigator.
58         /// If the page is already in Navigator, then it is not pushed.
59         /// </summary>
60         /// <param name="page">The page to push to Navigator.</param>
61         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
62         [EditorBrowsable(EditorBrowsableState.Never)]
63         public void Push(Page page)
64         {
65             if (page == null)
66             {
67                 throw new ArgumentNullException(nameof(page), "page should not be null.");
68             }
69
70             //Duplicate page is not pushed.
71             if (NavigationPages.Contains(page)) return;
72
73             var curTop = Peek();
74
75             if (!curTop)
76             {
77                 Insert(0, page);
78                 return;
79             }
80
81             NavigationPages.Add(page);
82             Add(page);
83
84             //Invoke Page events
85             page.InvokeAppearing();
86             curTop.InvokeDisappearing();
87
88             //TODO: The following transition codes will be replaced with view transition.
89             if (_curAnimation)
90             {
91                 _curAnimation.Stop();
92                 _curAnimation.Clear();
93             }
94
95             if (_newAnimation)
96             {
97                 _newAnimation.Stop();
98                 _newAnimation.Clear();
99             }
100
101             _curAnimation = new Animation(1000);
102             using (var scaleVec = new Vector3(0.0f, 0.0f, 1.0f))
103             {
104                 _curAnimation.AnimateTo(curTop, "Scale", scaleVec, 0, 1000);
105             }
106             _curAnimation.AnimateTo(curTop, "Opacity", 0.0f, 0, 1000);
107             _curAnimation.EndAction = Animation.EndActions.Discard;
108             _curAnimation.Play();
109
110             using (var scaleVec = new Vector3(0.0f, 0.0f, 1.0f))
111             {
112                 using (var scaleProp = new Tizen.NUI.PropertyValue(scaleVec))
113                 {
114                     Tizen.NUI.Object.SetProperty(page.SwigCPtr, Page.Property.SCALE, scaleProp);
115                 }
116             }
117             using (var scaleProp = new Tizen.NUI.PropertyValue(0.0f))
118             {
119                 Tizen.NUI.Object.SetProperty(page.SwigCPtr, Page.Property.OPACITY, scaleProp);
120             }
121             _newAnimation = new Animation(1000);
122             using (var scaleVec = new Vector3(1.0f, 1.0f, 1.0f))
123             {
124                 _newAnimation.AnimateTo(page, "Scale", scaleVec, 0, 1000);
125             }
126             _newAnimation.AnimateTo(page, "Opacity", 1.0f, 0, 1000);
127             _newAnimation.Play();
128         }
129
130         /// <summary>
131         /// Pops the top page from Navigator.
132         /// </summary>
133         /// <returns>The popped page.</returns>
134         /// <exception cref="InvalidOperationException">Thrown when there is no page in Navigator.</exception>
135         [EditorBrowsable(EditorBrowsableState.Never)]
136         public Page Pop()
137         {
138             if (NavigationPages.Count == 0)
139             {
140                 throw new InvalidOperationException("There is no page in Navigator.");
141             }
142
143             var curTop = Peek();
144
145             if (NavigationPages.Count == 1)
146             {
147                 Remove(curTop);
148                 return curTop;
149             }
150
151             var newTop = NavigationPages[NavigationPages.Count - 2];
152
153             //Invoke Page events
154             newTop.InvokeAppearing();
155             curTop.InvokeDisappearing();
156
157             //TODO: The following transition codes will be replaced with view transition.
158             if (_curAnimation)
159             {
160                 _curAnimation.Stop();
161                 _curAnimation.Clear();
162             }
163
164             if (_newAnimation)
165             {
166                 _newAnimation.Stop();
167                 _newAnimation.Clear();
168             }
169
170             _curAnimation = new Animation(1000);
171             using (var scaleVec = new Vector3(0.0f, 0.0f, 1.0f))
172             {
173                 _curAnimation.AnimateTo(curTop, "Scale", scaleVec, 0, 1000);
174             }
175             _curAnimation.AnimateTo(curTop, "Opacity", 0.0f, 0, 1000);
176             _curAnimation.Play();
177             _curAnimation.Finished += (object sender, EventArgs e) =>
178             {
179                 //Removes the current top page after transition is finished.
180                 Remove(curTop);
181             };
182
183             using (var scaleVec = new Vector3(0.0f, 0.0f, 1.0f))
184             {
185                 using (var scaleProp = new Tizen.NUI.PropertyValue(scaleVec))
186                 {
187                     Tizen.NUI.Object.SetProperty(newTop.SwigCPtr, Page.Property.SCALE, scaleProp);
188                 }
189             }
190             using (var opacityProp = new Tizen.NUI.PropertyValue(0.0f))
191             {
192                 Tizen.NUI.Object.SetProperty(newTop.SwigCPtr, Page.Property.OPACITY, opacityProp);
193             }
194             _newAnimation = new Animation(1000);
195             using (var scaleVec = new Vector3(1.0f, 1.0f, 1.0f))
196             {
197                 _newAnimation.AnimateTo(newTop, "Scale", scaleVec, 0, 1000);
198             }
199             _newAnimation.AnimateTo(newTop, "Opacity", 1.0f, 0, 1000);
200             _newAnimation.Play();
201
202             return curTop;
203         }
204
205         /// <summary>
206         /// Inserts a page at the specified index of Navigator.
207         /// If the page is already in Navigator, then it is not inserted.
208         /// </summary>
209         /// <param name="index">The index of Navigator where a page will be inserted.</param>
210         /// <param name="page">The page to insert to Navigator.</param>
211         /// <exception cref="ArgumentOutOfRangeException">Thrown when the argument index is less than 0, or greater than the number of pages.</exception>
212         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
213         [EditorBrowsable(EditorBrowsableState.Never)]
214         public void Insert(int index, Page page)
215         {
216             if ((index < 0) || (index > NavigationPages.Count))
217             {
218                 throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than or equal to the number of pages.");
219             }
220
221             if (page == null)
222             {
223                 throw new ArgumentNullException(nameof(page), "page should not be null.");
224             }
225
226             //Duplicate page is not pushed.
227             if (NavigationPages.Contains(page)) return;
228
229             NavigationPages.Insert(index, page);
230             Add(page);
231         }
232
233         /// <summary>
234         /// Inserts a page to Navigator before an existing page.
235         /// If the page is already in Navigator, then it is not inserted.
236         /// </summary>
237         /// <param name="before">The existing page, before which a page will be inserted.</param>
238         /// <param name="page">The page to insert to Navigator.</param>
239         /// <exception cref="ArgumentNullException">Thrown when the argument before is null.</exception>
240         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
241         /// <exception cref="ArgumentException">Thrown when the argument before does not exist in Navigator.</exception>
242         [EditorBrowsable(EditorBrowsableState.Never)]
243         public void InsertBefore(Page before, Page page)
244         {
245             if (before == null)
246             {
247                 throw new ArgumentNullException(nameof(before), "before should not be null.");
248             }
249
250             if (page == null)
251             {
252                 throw new ArgumentNullException(nameof(page), "page should not be null.");
253             }
254
255             //Find the index of before page.
256             int beforeIndex = NavigationPages.FindIndex(x => x == before);
257
258             //before does not exist in Navigator.
259             if (beforeIndex == -1)
260             {
261                 throw new ArgumentException("before does not exist in Navigator.", nameof(before));
262             }
263
264             Insert(beforeIndex, page);
265         }
266
267         /// <summary>
268         /// Removes a page from Navigator.
269         /// </summary>
270         /// <param name="page">The page to remove from Navigator.</param>
271         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
272         [EditorBrowsable(EditorBrowsableState.Never)]
273         public void Remove(Page page)
274         {
275             if (page == null)
276             {
277                 throw new ArgumentNullException(nameof(page), "page should not be null.");
278             }
279
280             NavigationPages.Remove(page);
281             base.Remove(page);
282         }
283
284         /// <summary>
285         /// Removes a page at the specified index of Navigator.
286         /// </summary>
287         /// <param name="index">The index of Navigator where a page will be removed.</param>
288         /// <exception cref="ArgumentOutOfRangeException">Thrown when the index is less than 0, or greater than or equal to the number of pages.</exception>
289         [EditorBrowsable(EditorBrowsableState.Never)]
290         public void RemoveAt(int index)
291         {
292             if ((index < 0) || (index >= NavigationPages.Count))
293             {
294                 throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than the number of pages.");
295             }
296
297             Remove(NavigationPages[index]);
298         }
299
300         /// <summary>
301         /// Returns the page at the top of Navigator.
302         /// </summary>
303         /// <returns>The page at the top of Navigator.</returns>
304         [EditorBrowsable(EditorBrowsableState.Never)]
305         public Page Peek()
306         {
307             if (NavigationPages.Count == 0) return null;
308
309             return NavigationPages[NavigationPages.Count - 1];
310         }
311
312         /// <summary>
313         /// Returns the default navigator of the given window.
314         /// </summary>
315         /// <returns>The default navigator of the given window.</returns>
316         /// <exception cref="ArgumentNullException">Thrown when the argument window is null.</exception>
317         [EditorBrowsable(EditorBrowsableState.Never)]
318         public static Navigator GetDefaultNavigator(Window window)
319         {
320             if (window == null)
321             {
322                 throw new ArgumentNullException(nameof(window), "window should not be null.");
323             }
324
325             if (windowNavigator.ContainsKey(window) == true)
326             {
327                 return windowNavigator[window];
328             }
329
330             var defaultNavigator = new Navigator();
331             defaultNavigator.WidthResizePolicy = ResizePolicyType.FillToParent;
332             defaultNavigator.HeightResizePolicy = ResizePolicyType.FillToParent;
333             window.Add(defaultNavigator);
334             windowNavigator.Add(window, defaultNavigator);
335
336             return defaultNavigator;
337         }
338
339         /// <summary>
340         /// Shows a dialog by pushing a page containing dialog to default navigator.
341         /// </summary>
342         /// <param name="content">The content of Dialog.</param>
343         [EditorBrowsable(EditorBrowsableState.Never)]
344         public static void ShowDialog(View content = null)
345         {
346             var window = NUIApplication.GetDefaultWindow();
347             var defaultNavigator = window.GetDefaultNavigator();
348
349             var dialog = new Dialog(content);
350             SetDialogScrim(dialog);
351
352             var dialogPage = new Page(dialog);
353             defaultNavigator.Push(dialogPage);
354         }
355
356         /// <summary>
357         /// Shows an alert dialog by pushing a page containing the alert dialog
358         /// to default navigator.
359         /// </summary>
360         /// <param name="titleContent">The title content of AlertDialog.</param>
361         /// <param name="content">The content of AlertDialog.</param>
362         /// <param name="actionContent">The action content of AlertDialog.</param>
363         [EditorBrowsable(EditorBrowsableState.Never)]
364         public static void ShowAlertDialog(View titleContent, View content, View actionContent)
365         {
366             var window = NUIApplication.GetDefaultWindow();
367             var defaultNavigator = window.GetDefaultNavigator();
368
369             var dialog = new AlertDialog(titleContent, content, actionContent);
370             SetDialogScrim(dialog);
371
372             var dialogPage = new Page(dialog);
373             defaultNavigator.Push(dialogPage);
374         }
375
376         /// <summary>
377         /// Shows an alert dialog by pushing a page containing the alert dialog
378         /// to default navigator.
379         /// </summary>
380         /// <param name="title">The title of AlertDialog.</param>
381         /// <param name="message">The message of AlertDialog.</param>
382         /// <param name="positiveButtonText">The positive button text in the action content of AlertDialog.</param>
383         /// <param name="positiveButtonClickedHandler">The clicked callback of the positive button in the action content of AlertDialog.</param>
384         /// <param name="negativeButtonText">The negative button text in the action content of AlertDialog.</param>
385         /// <param name="negativeButtonClickedHandler">The clicked callback of the negative button in the action content of AlertDialog.</param>
386         [EditorBrowsable(EditorBrowsableState.Never)]
387         public static void ShowAlertDialog(string title = null, string message = null, string positiveButtonText = null, EventHandler<ClickedEventArgs> positiveButtonClickedHandler = null, string negativeButtonText = null, EventHandler<ClickedEventArgs> negativeButtonClickedHandler = null)
388         {
389             var window = NUIApplication.GetDefaultWindow();
390             var defaultNavigator = window.GetDefaultNavigator();
391
392             var dialog = new AlertDialog(title, message, positiveButtonText, positiveButtonClickedHandler, negativeButtonText, negativeButtonClickedHandler);
393             SetDialogScrim(dialog);
394
395             var dialogPage = new Page(dialog);
396             defaultNavigator.Push(dialogPage);
397         }
398
399
400         private static void SetDialogScrim(Dialog dialog)
401         {
402             if (dialog == null)
403             {
404                 return;
405             }
406
407             var window = NUIApplication.GetDefaultWindow();
408             var defaultNavigator = window.GetDefaultNavigator();
409             var defaultScrim = dialog.Scrim;
410
411             //Copies default scrim's GUI properties.
412             var scrim = new VisualView();
413             scrim.BackgroundColor = defaultScrim.BackgroundColor;
414             scrim.Size = defaultScrim.Size;
415             scrim.TouchEvent += (object source, View.TouchEventArgs e) =>
416             {
417                 if (e.Touch.GetState(0) == PointStateType.Up)
418                 {
419                     defaultNavigator.Pop();
420                 }
421
422                 return true;
423             };
424
425             dialog.Scrim = scrim;
426         }
427     }
428 } //namespace Tizen.NUI