[NUI] Fix Navigator Transition issue
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / Navigation / Navigator.cs
1 /*
2  * Copyright(c) 2021 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 Tizen.NUI.BaseComponents;
22
23 namespace Tizen.NUI.Components
24 {
25     /// <summary>
26     /// The Navigator is a class which navigates pages with stack methods such as Push and Pop.
27     /// </summary>
28     /// <remarks>
29     /// With Transition class, Navigator supports smooth transition of View pair between two Pages
30     /// by using <see cref="PushWithTransition(Page)"/> and <see cref="PopWithTransition()"/> methods.
31     /// If current top Page and next top Page have <see cref="View"/>s those have same TransitionTag,
32     /// Navigator creates smooth transition motion for them.
33     /// Navigator.Transition property can be used to set properties of the Transition such as TimePeriod and AlphaFunction.
34     /// When all transitions are finished, Navigator calls a callback methods those connected on the "TransitionFinished" event.
35     /// </remarks>
36     /// <example>
37     /// <code>
38     /// Navigator navigator = new Navigator()
39     /// {
40     ///     TimePeriod = new TimePeriod(500),
41     ///     AlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.EaseInOutSine)
42     /// };
43     ///
44     /// View view = new View()
45     /// {
46     ///     TransitionOptions = new TransitionOptions()
47     ///     {
48     ///         /* Set properties for the transition of this View */
49     ///     }
50     /// };
51     ///
52     /// ContentPage newPage = new ContentPage()
53     /// {
54     ///     Content = view,
55     /// };
56     ///
57     /// Navigator.PushWithTransition(newPage);
58     /// </code>
59     /// </example>
60     /// <since_tizen> 9 </since_tizen>
61     public class Navigator : Control
62     {
63         private const int DefaultTransitionDuration = 500;
64
65         //This will be replaced with view transition class instance.
66         private Animation curAnimation = null;
67
68         //This will be replaced with view transition class instance.
69         private Animation newAnimation = null;
70
71         private TransitionSet transitionSet = null;
72
73         private Transition transition = new Transition()
74         {
75             TimePeriod = new TimePeriod(DefaultTransitionDuration),
76             AlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.Default),
77         };
78
79         private bool transitionFinished = true;
80
81         //TODO: Needs to consider how to remove disposed window from dictionary.
82         //Two dictionaries are required to remove disposed navigator from dictionary.
83         private static Dictionary<Window, Navigator> windowNavigator = new Dictionary<Window, Navigator>();
84         private static Dictionary<Navigator, Window> navigatorWindow = new Dictionary<Navigator, Window>();
85
86         private List<Page> navigationPages = new List<Page>();
87
88         /// <summary>
89         /// Creates a new instance of a Navigator.
90         /// </summary>
91         /// <since_tizen> 9 </since_tizen>
92         public Navigator() : base()
93         {
94             Layout = new AbsoluteLayout();
95         }
96         
97         /// <summary>
98         /// An event fired when Transition has been finished.
99         /// </summary>
100         /// <since_tizen> 9 </since_tizen>
101         public event EventHandler<EventArgs> TransitionFinished;
102
103         /// <summary>
104         /// Returns the count of pages in Navigator.
105         /// </summary>
106         /// <since_tizen> 9 </since_tizen>
107         public int PageCount => navigationPages.Count;
108
109         /// <summary>
110         /// Transition properties for the transition of View pair having same transition tag.
111         /// </summary>
112         /// <since_tizen> 9 </since_tizen>
113         public Transition Transition
114         {
115             set
116             {
117                 transition = value;
118             }
119             get
120             {
121                 return transition;
122             }
123         }
124
125         /// <summary>
126         /// Pushes a page to Navigator.
127         /// If the page is already in Navigator, then it is not pushed.
128         /// </summary>
129         /// <param name="page">The page to push to Navigator.</param>
130         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
131         /// <since_tizen> 9 </since_tizen>
132         public void PushWithTransition(Page page)
133         {
134             if (!transitionFinished)
135             {
136                 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
137                 return;
138             }
139
140             if (page == null)
141             {
142                 throw new ArgumentNullException(nameof(page), "page should not be null.");
143             }
144
145             //Duplicate page is not pushed.
146             if (navigationPages.Contains(page)) return;
147
148             var topPage = Peek();
149
150             if (!topPage)
151             {
152                 Insert(0, page);
153                 return;
154             }
155
156             navigationPages.Add(page);
157             Add(page);
158             page.Navigator = this;
159
160             //Invoke Page events
161             page.InvokeAppearing();
162             topPage.InvokeDisappearing();
163
164             transitionSet = CreateTransition(topPage, page, true);
165             transitionSet.Finished += (object sender, EventArgs e) =>
166             {
167                 topPage.SetVisible(false);
168
169                 //Invoke Page events
170                 page.InvokeAppeared();
171                 topPage.InvokeDisappeared();
172             };
173             transitionFinished = false;
174         }
175
176         /// <summary>
177         /// Pops the top page from Navigator.
178         /// </summary>
179         /// <returns>The popped page.</returns>
180         /// <exception cref="InvalidOperationException">Thrown when there is no page in Navigator.</exception>
181         /// <since_tizen> 9 </since_tizen>
182         public Page PopWithTransition()
183         {
184             if (!transitionFinished)
185             {
186                 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
187                 return null;
188             }
189
190             if (navigationPages.Count == 0)
191             {
192                 throw new InvalidOperationException("There is no page in Navigator.");
193             }
194
195             var topPage = Peek();
196
197             if (navigationPages.Count == 1)
198             {
199                 Remove(topPage);
200                 return topPage;
201             }
202             var newTopPage = navigationPages[navigationPages.Count - 2];
203
204 //            newTopPage.RaiseAbove(topPage);
205
206             //Invoke Page events
207             newTopPage.InvokeAppearing();
208             topPage.InvokeDisappearing();
209
210             transitionSet = CreateTransition(topPage, newTopPage, false);
211             transitionSet.Finished += (object sender, EventArgs e) =>
212             {
213                 Remove(topPage);
214                 topPage.SetVisible(true);
215
216                 //Invoke Page events
217                 newTopPage.InvokeAppeared();
218                 topPage.InvokeDisappeared();
219             };
220             transitionFinished = false;
221
222             return topPage;
223         }
224
225         /// <summary>
226         /// Pushes a page to Navigator.
227         /// If the page is already in Navigator, then it is not pushed.
228         /// </summary>
229         /// <param name="page">The page to push to Navigator.</param>
230         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
231         /// <since_tizen> 9 </since_tizen>
232         public void Push(Page page)
233         {
234             if (!transitionFinished)
235             {
236                 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
237                 return;
238             }
239
240             if (page == null)
241             {
242                 throw new ArgumentNullException(nameof(page), "page should not be null.");
243             }
244
245             //Duplicate page is not pushed.
246             if (navigationPages.Contains(page)) return;
247
248             var curTop = Peek();
249
250             if (!curTop)
251             {
252                 Insert(0, page);
253                 return;
254             }
255
256             navigationPages.Add(page);
257             Add(page);
258             page.Navigator = this;
259
260             //Invoke Page events
261             page.InvokeAppearing();
262             curTop.InvokeDisappearing();
263
264             //TODO: The following transition codes will be replaced with view transition.
265             InitializeAnimation();
266
267             if (page is DialogPage == false)
268             {
269                 curAnimation = new Animation(1000);
270                 curAnimation.AnimateTo(curTop, "Opacity", 0.0f, 0, 1000);
271                 curAnimation.EndAction = Animation.EndActions.StopFinal;
272                 curAnimation.Finished += (object sender, EventArgs args) =>
273                 {
274                     curTop.SetVisible(false);
275
276                     //Invoke Page events
277                     curTop.InvokeDisappeared();
278                 };
279                 curAnimation.Play();
280
281                 page.Opacity = 0.0f;
282                 page.SetVisible(true);
283                 newAnimation = new Animation(1000);
284                 newAnimation.AnimateTo(page, "Opacity", 1.0f, 0, 1000);
285                 newAnimation.EndAction = Animation.EndActions.StopFinal;
286                 newAnimation.Finished += (object sender, EventArgs e) =>
287                 {
288                     //Invoke Page events
289                     page.InvokeAppeared();
290                 };
291                 newAnimation.Play();
292             }
293         }
294
295         /// <summary>
296         /// Pops the top page from Navigator.
297         /// </summary>
298         /// <returns>The popped page.</returns>
299         /// <exception cref="InvalidOperationException">Thrown when there is no page in Navigator.</exception>
300         /// <since_tizen> 9 </since_tizen>
301         public Page Pop()
302         {
303             if (!transitionFinished)
304             {
305                 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
306                 return null;
307             }
308
309             if (navigationPages.Count == 0)
310             {
311                 throw new InvalidOperationException("There is no page in Navigator.");
312             }
313
314             var curTop = Peek();
315
316             if (navigationPages.Count == 1)
317             {
318                 Remove(curTop);
319                 return curTop;
320             }
321
322             var newTop = navigationPages[navigationPages.Count - 2];
323
324             //Invoke Page events
325             newTop.InvokeAppearing();
326             curTop.InvokeDisappearing();
327
328             //TODO: The following transition codes will be replaced with view transition.
329             InitializeAnimation();
330
331             if (curTop is DialogPage == false)
332             {
333                 curAnimation = new Animation(1000);
334                 curAnimation.AnimateTo(curTop, "Opacity", 0.0f, 0, 1000);
335                 curAnimation.EndAction = Animation.EndActions.StopFinal;
336                 curAnimation.Finished += (object sender, EventArgs e) =>
337                 {
338                     //Removes the current top page after transition is finished.
339                     Remove(curTop);
340                     curTop.Opacity = 1.0f;
341
342                     //Invoke Page events
343                     curTop.InvokeDisappeared();
344                 };
345                 curAnimation.Play();
346
347                 newTop.Opacity = 0.0f;
348                 newTop.SetVisible(true);
349                 newAnimation = new Animation(1000);
350                 newAnimation.AnimateTo(newTop, "Opacity", 1.0f, 0, 1000);
351                 newAnimation.EndAction = Animation.EndActions.StopFinal;
352                 newAnimation.Finished += (object sender, EventArgs e) =>
353                 {
354                     //Invoke Page events
355                     newTop.InvokeAppeared();
356                 };
357                 newAnimation.Play();
358             }
359             else
360             {
361                 Remove(curTop);
362             }
363
364             return curTop;
365         }
366
367         /// <summary>
368         /// Returns the page of the given index in Navigator.
369         /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
370         /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
371         /// </summary>
372         /// <param name="index">The index of a page in Navigator.</param>
373         /// <returns>The page of the given index in Navigator.</returns>
374         /// <exception cref="ArgumentOutOfRangeException">Thrown when the argument index is less than 0, or greater than the number of pages.</exception>
375         public Page GetPage(int index)
376         {
377             if ((index < 0) || (index > navigationPages.Count))
378             {
379                 throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than or equal to the number of pages.");
380             }
381
382             return navigationPages[index];
383         }
384
385         /// <summary>
386         /// Returns the current index of the given page in Navigator.
387         /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
388         /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
389         /// </summary>
390         /// <param name="page">The page in Navigator.</param>
391         /// <returns>The index of the given page in Navigator. If the given page is not in the Navigator, then -1 is returned.</returns>
392         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
393         /// <since_tizen> 9 </since_tizen>
394         public int IndexOf(Page page)
395         {
396             if (page == null)
397             {
398                 throw new ArgumentNullException(nameof(page), "page should not be null.");
399             }
400
401             for (int i = 0; i < navigationPages.Count; i++)
402             {
403                 if (navigationPages[i] == page)
404                 {
405                     return i;
406                 }
407             }
408
409             return -1;
410         }
411
412         /// <summary>
413         /// Inserts a page at the specified index of Navigator.
414         /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
415         /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
416         /// To find the current index of a page in Navigator, please use IndexOf(page).
417         /// If the page is already in Navigator, then it is not inserted.
418         /// </summary>
419         /// <param name="index">The index of a page in Navigator where the page will be inserted.</param>
420         /// <param name="page">The page to insert to Navigator.</param>
421         /// <exception cref="ArgumentOutOfRangeException">Thrown when the argument index is less than 0, or greater than the number of pages.</exception>
422         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
423         /// <since_tizen> 9 </since_tizen>
424         public void Insert(int index, Page page)
425         {
426             if ((index < 0) || (index > navigationPages.Count))
427             {
428                 throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than or equal to the number of pages.");
429             }
430
431             if (page == null)
432             {
433                 throw new ArgumentNullException(nameof(page), "page should not be null.");
434             }
435
436             //Duplicate page is not pushed.
437             if (navigationPages.Contains(page)) return;
438
439             //TODO: The following transition codes will be replaced with view transition.
440             InitializeAnimation();
441
442             if (index == PageCount)
443             {
444                 page.Opacity = 1.0f;
445                 page.SetVisible(true);
446             }
447             else
448             {
449                 page.SetVisible(false);
450                 page.Opacity = 0.0f;
451             }
452
453             navigationPages.Insert(index, page);
454             Add(page);
455             page.Navigator = this;
456         }
457
458         /// <summary>
459         /// Inserts a page to Navigator before an existing page.
460         /// If the page is already in Navigator, then it is not inserted.
461         /// </summary>
462         /// <param name="before">The existing page, before which a page will be inserted.</param>
463         /// <param name="page">The page to insert to Navigator.</param>
464         /// <exception cref="ArgumentNullException">Thrown when the argument before is null.</exception>
465         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
466         /// <exception cref="ArgumentException">Thrown when the argument before does not exist in Navigator.</exception>
467         /// <since_tizen> 9 </since_tizen>
468         public void InsertBefore(Page before, Page page)
469         {
470             if (before == null)
471             {
472                 throw new ArgumentNullException(nameof(before), "before should not be null.");
473             }
474
475             if (page == null)
476             {
477                 throw new ArgumentNullException(nameof(page), "page should not be null.");
478             }
479
480             //Find the index of before page.
481             int beforeIndex = navigationPages.FindIndex(x => x == before);
482
483             //before does not exist in Navigator.
484             if (beforeIndex == -1)
485             {
486                 throw new ArgumentException("before does not exist in Navigator.", nameof(before));
487             }
488
489             Insert(beforeIndex, page);
490         }
491
492         /// <summary>
493         /// Removes a page from Navigator.
494         /// </summary>
495         /// <param name="page">The page to remove from Navigator.</param>
496         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
497         /// <since_tizen> 9 </since_tizen>
498         public void Remove(Page page)
499         {
500             if (page == null)
501             {
502                 throw new ArgumentNullException(nameof(page), "page should not be null.");
503             }
504
505             //TODO: The following transition codes will be replaced with view transition.
506             InitializeAnimation();
507
508             if ((page == Peek()) && (PageCount >= 2))
509             {
510                 navigationPages[PageCount - 2].Opacity = 1.0f;
511                 navigationPages[PageCount - 2].SetVisible(true);
512             }
513
514             page.Navigator = null;
515             navigationPages.Remove(page);
516             base.Remove(page);
517         }
518
519         /// <summary>
520         /// Removes a page at the specified index of Navigator.
521         /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
522         /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
523         /// To find the current index of a page in Navigator, please use IndexOf(page).
524         /// </summary>
525         /// <param name="index">The index of a page in Navigator where the page will be removed.</param>
526         /// <exception cref="ArgumentOutOfRangeException">Thrown when the index is less than 0, or greater than or equal to the number of pages.</exception>
527         /// <since_tizen> 9 </since_tizen>
528         public void RemoveAt(int index)
529         {
530             if ((index < 0) || (index >= navigationPages.Count))
531             {
532                 throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than the number of pages.");
533             }
534
535             Remove(navigationPages[index]);
536         }
537
538         /// <summary>
539         /// Returns the page at the top of Navigator.
540         /// </summary>
541         /// <returns>The page at the top of Navigator.</returns>
542         /// <since_tizen> 9 </since_tizen>
543         public Page Peek()
544         {
545             if (navigationPages.Count == 0) return null;
546
547             return navigationPages[navigationPages.Count - 1];
548         }
549
550         /// <summary>
551         /// Disposes Navigator and all children on it.
552         /// </summary>
553         /// <param name="type">Dispose type.</param>
554         [EditorBrowsable(EditorBrowsableState.Never)]
555         protected override void Dispose(DisposeTypes type)
556         {
557             if (disposed)
558             {
559                 return;
560             }
561
562             if (type == DisposeTypes.Explicit)
563             {
564                 foreach (Page page in navigationPages)
565                 {
566                     Utility.Dispose(page);
567                 }
568                 navigationPages.Clear();
569
570                 Window window;
571
572                 if (navigatorWindow.TryGetValue(this, out window) == true)
573                 {
574                     navigatorWindow.Remove(this);
575                     windowNavigator.Remove(window);
576                 }
577             }
578
579             base.Dispose(type);
580         }
581
582         /// <summary>
583         /// Returns the default navigator of the given window.
584         /// </summary>
585         /// <returns>The default navigator of the given window.</returns>
586         /// <exception cref="ArgumentNullException">Thrown when the argument window is null.</exception>
587         /// <since_tizen> 9 </since_tizen>
588         public static Navigator GetDefaultNavigator(Window window)
589         {
590             if (window == null)
591             {
592                 throw new ArgumentNullException(nameof(window), "window should not be null.");
593             }
594
595             if (windowNavigator.ContainsKey(window) == true)
596             {
597                 return windowNavigator[window];
598             }
599
600             var defaultNavigator = new Navigator();
601             defaultNavigator.WidthResizePolicy = ResizePolicyType.FillToParent;
602             defaultNavigator.HeightResizePolicy = ResizePolicyType.FillToParent;
603             window.Add(defaultNavigator);
604             windowNavigator.Add(window, defaultNavigator);
605             navigatorWindow.Add(defaultNavigator, window);
606
607             return defaultNavigator;
608         }
609
610         /// <summary>
611         /// Create Transition between currentTopPage and newTopPage
612         /// </summary>
613         /// <param name="currentTopPage">The top page of Navigator.</param>
614         /// <param name="newTopPage">The new top page after transition.</param>
615         /// <param name="pushTransition">True if this transition is for push new page</param>
616         private TransitionSet CreateTransition(Page currentTopPage, Page newTopPage, bool pushTransition)
617         {
618             currentTopPage.SetVisible(true);
619             newTopPage.SetVisible(true);
620
621             List<View> taggedViewsInNewTopPage = new List<View>();
622             RetrieveTaggedViews(taggedViewsInNewTopPage, newTopPage);
623             List<View> taggedViewsInCurrentTopPage = new List<View>();
624             RetrieveTaggedViews(taggedViewsInCurrentTopPage, currentTopPage);
625
626             List<KeyValuePair<View, View>> sameTaggedViewPair = new List<KeyValuePair<View, View>>();
627             foreach(View currentTopPageView in taggedViewsInCurrentTopPage)
628             {
629                 bool findPair = false;
630                 foreach(View newTopPageView in taggedViewsInNewTopPage)
631                 {
632                     if((currentTopPageView.TransitionOptions != null) && (newTopPageView.TransitionOptions != null) &&
633                         currentTopPageView.TransitionOptions?.TransitionTag == newTopPageView.TransitionOptions?.TransitionTag)
634                     {
635                         sameTaggedViewPair.Add(new KeyValuePair<View, View>(currentTopPageView, newTopPageView));
636                         findPair = true;
637                         break;
638                     }
639                 }
640                 if(findPair)
641                 {
642                     taggedViewsInNewTopPage.Remove(sameTaggedViewPair[sameTaggedViewPair.Count - 1].Value);
643                 }
644             }
645             foreach(KeyValuePair<View, View> pair in sameTaggedViewPair)
646             {
647                 taggedViewsInCurrentTopPage.Remove(pair.Key);
648             }
649
650             TransitionSet newTransitionSet = new TransitionSet();
651             foreach(KeyValuePair<View, View> pair in sameTaggedViewPair)
652             {
653                 TransitionItem pairTransition = transition.CreateTransition(pair.Key, pair.Value);
654                 if(pair.Value.TransitionOptions?.TransitionWithChild ?? false)
655                 {
656                     pairTransition.TransitionWithChild = true;
657                 }
658                 newTransitionSet.AddTransition(pairTransition);
659             }
660
661             newTransitionSet.Finished += (object sender, EventArgs e) =>
662             {
663                 transitionFinished = true;
664                 InvokeTransitionFinished();
665                 transitionSet.Dispose();
666                 currentTopPage.Opacity = 1.0f;
667             };
668
669             // default appearing/disappearing transition - fast fade (half duration compaired with that of view pair transition)
670             int duration = (transition.TimePeriod.DurationMilliseconds + transition.TimePeriod.DelayMilliseconds);
671             float durationSeconds = (float)duration / 1000.0f;
672
673             TransitionItemBase disappearingTransition = currentTopPage.DisappearingTransition.CreateTransition(currentTopPage, false);
674             TransitionItemBase appearingTransition = newTopPage.AppearingTransition.CreateTransition(newTopPage, true);
675             disappearingTransition.TransitionWithChild = true;
676             appearingTransition.TransitionWithChild = true;
677             newTransitionSet.AddTransition(disappearingTransition);
678             newTransitionSet.AddTransition(appearingTransition);
679
680             newTransitionSet.Play();
681
682             return newTransitionSet;
683         }
684
685         /// <summary>
686         /// Retrieve Tagged Views in the view tree.
687         /// </summary>
688         /// <param name="taggedViews">Returned tagged view list..</param>
689         /// <param name="view">Root View to get tagged child View.</param>
690         private void RetrieveTaggedViews(List<View> taggedViews, View view)
691         {
692             if (!string.IsNullOrEmpty(view.TransitionOptions?.TransitionTag))
693             {
694                 taggedViews.Add((view as View));
695             }
696
697             if (view.ChildCount == 0)
698             {
699                 return;
700             }
701
702             if (view.TransitionOptions?.TransitionWithChild ?? false)
703             {
704                 return;
705             }
706
707             foreach (View child in view.Children)
708             {
709                 RetrieveTaggedViews(taggedViews, child);
710             }
711         }
712
713         internal void InvokeTransitionFinished()
714         {
715             TransitionFinished?.Invoke(this, new EventArgs());
716         }
717
718         //TODO: The following transition codes will be replaced with view transition.
719         private void InitializeAnimation()
720         {
721             if (curAnimation != null)
722             {
723                 curAnimation.Stop();
724                 curAnimation.Clear();
725                 curAnimation = null;
726             }
727
728             if (newAnimation != null)
729             {
730                 newAnimation.Stop();
731                 newAnimation.Clear();
732                 newAnimation = null;
733             }
734         }
735     }
736 }