[NUI] Add SetMinMaxFrameByMarker() in LottieAnimationView (#1210)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / BaseComponents / LottieAnimationView.cs
1 /*
2  * Copyright(c) 2019 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 global::System;
19 using global::System.Runtime.InteropServices;
20 using System.ComponentModel;
21 using System.Collections.Generic;
22
23 #if (NUI_DEBUG_ON)
24 using tlog = Tizen.Log;
25 #endif
26
27 namespace Tizen.NUI.BaseComponents
28 {
29     /// <summary>
30     /// LottieAnimationView renders an animated vector image (Lottie file).
31     /// </summary>
32     /// <since_tizen> 7 </since_tizen>
33     public class LottieAnimationView : ImageView
34     {
35         #region Constructor, Distructor, Dispose
36         /// <summary>
37         /// LottieAnimationView constructor
38         /// </summary>
39         /// <param name="scale">The factor of scaling image, default : 1.0f</param>
40         /// <param name="shown">false : not displayed (hidden), true : displayed (shown), default : true</param>
41         /// <remarks>
42         /// If shown parameter is false, it is not visible even the LottieAnimationView instance is created.
43         /// </remarks>
44         /// <example>
45         /// <code>
46         /// LottieAnimationView myLottie = new LottieAnimationView();
47         /// LottieAnimationView myLottie2 = new LottieAnimationView(2.0f);
48         /// LottieAnimationView myLottie3 = new LottieAnimationView(1.0f, false);
49         /// </code>
50         /// </example>
51         /// <since_tizen> 7 </since_tizen>
52         public LottieAnimationView(float scale = 1.0f, bool shown = true) : base()
53         {
54             tlog.Fatal(tag, $"< constructor GetId={GetId()} >");
55             currentStates.url = "";
56             currentStates.frame = -1;
57             currentStates.loopCount = 1;
58             currentStates.loopMode = LoopingModeType.Restart;
59             currentStates.stopEndAction = StopBehaviorType.CurrentFrame;
60             currentStates.framePlayRangeMin = -1;
61             currentStates.framePlayRangeMax = -1;
62             currentStates.changed = false;
63             currentStates.totalFrame = -1;
64             currentStates.scale = scale;
65             SetVisible(shown);
66         }
67
68         /// <summary>
69         /// Dispose(DisposeTypes type)
70         /// </summary>
71         /// <param name="type"></param>
72         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
73         [EditorBrowsable(EditorBrowsableState.Never)]
74         protected override void Dispose(DisposeTypes type)
75         {
76             if (disposed)
77             {
78                 return;
79             }
80
81             tlog.Fatal(tag, $"<[{GetId()}] type={type}");
82
83             //Release your own unmanaged resources here.
84             //You should not access any managed member here except static instance.
85             //because the execution order of Finalizes is non-deterministic.
86
87             //disconnect event signal
88             if (finishedEventHandler != null && visualEventSignalCallback != null)
89             {
90                 VisualEventSignal().Disconnect(visualEventSignalCallback);
91                 finishedEventHandler = null;
92                 tlog.Fatal(tag, $"disconnect event signal");
93             }
94
95             base.Dispose(type);
96             tlog.Fatal(tag, $"[{GetId()}]>");
97         }
98         #endregion Constructor, Distructor, Dispose
99
100
101         #region Property
102         /// <summary>
103         /// Set or Get resource URL of Lottie file.
104         /// </summary>
105         /// <since_tizen> 7 </since_tizen>
106         public string URL
107         {
108             set
109             {
110                 string ret = (value == null ? "" : value);
111                 currentStates.url = ret;
112                 currentStates.changed = true;
113
114                 tlog.Fatal(tag, $"<[{GetId()}]SET url={currentStates.url}");
115
116                 PropertyMap map = new PropertyMap();
117                 map.Add(Visual.Property.Type, new PropertyValue((int)DevelVisual.Type.AnimatedVectorImage))
118                     .Add(ImageVisualProperty.URL, new PropertyValue(currentStates.url))
119                     .Add(ImageVisualProperty.LoopCount, new PropertyValue(currentStates.loopCount))
120                     .Add(ImageVisualProperty.StopBehavior, new PropertyValue((int)currentStates.stopEndAction))
121                     .Add(ImageVisualProperty.LoopingMode, new PropertyValue((int)currentStates.loopMode));
122                 Image = map;
123
124                 currentStates.contentInfo = null;
125
126                 if (currentStates.scale != 1.0f)
127                 {
128                     Scale = new Vector3(currentStates.scale, currentStates.scale, 0.0f);
129                 }
130                 tlog.Fatal(tag, $"<[{GetId()}]>");
131             }
132             get
133             {
134                 string ret = currentStates.url;
135                 tlog.Fatal(tag, $"<[{GetId()}] GET");
136
137                 PropertyMap map = Image;
138                 if (map != null)
139                 {
140                     PropertyValue val = map.Find(ImageVisualProperty.URL);
141                     if (val != null)
142                     {
143                         if (val.Get(out ret))
144                         {
145                             tlog.Fatal(tag, $"gotten url={ret} >");
146                             return ret;
147                         }
148                     }
149                 }
150                 Tizen.Log.Error(tag, $"  [ERROR][{GetId()}](LottieAnimationView) Fail to get URL from dali >");
151                 return ret;
152             }
153         }
154
155         /// <summary>
156         /// Get playing state
157         /// </summary>
158         /// <since_tizen> 7 </since_tizen>
159         public PlayStateType PlayState
160         {
161             get
162             {
163                 tlog.Fatal(tag, $"< Get!");
164                 PropertyMap map = base.Image;
165                 var ret = 0;
166                 if (map != null)
167                 {
168                     PropertyValue val = map.Find(ImageVisualProperty.PlayState);
169                     if (val != null)
170                     {
171                         if (val.Get(out ret))
172                         {
173                             currentStates.playState = (PlayStateType)ret;
174                             tlog.Fatal(tag, $"gotten play state={ret} >");
175                             return currentStates.playState;
176                         }
177                     }
178                 }
179                 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}]Fail to get PlayState from dali currentStates.playState={currentStates.playState}>");
180                 return currentStates.playState;
181             }
182         }
183
184         /// <summary>
185         /// Get the number of total frame
186         /// </summary>
187         /// <since_tizen> 7 </since_tizen>
188         public int TotalFrame
189         {
190             get
191             {
192                 int ret = -1;
193                 PropertyMap map = Image;
194                 if (map != null)
195                 {
196                     PropertyValue val = map.Find(ImageVisualProperty.TotalFrameNumber);
197                     if (val != null)
198                     {
199                         if (val.Get(out ret))
200                         {
201                             //tlog.Fatal(tag,  $"TotalFrameNumber get! ret={ret}");
202                             currentStates.totalFrame = ret;
203                             return ret;
204                         }
205                     }
206                 }
207                 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get TotalFrameNumber from dali>");
208                 return ret;
209             }
210         }
211
212         /// <summary>
213         /// Set or Get current frame. When setting a specific frame, it is displayed as a still image.
214         /// </summary>
215         /// <remarks>
216         /// Giving user set value when getting. If the out ranged frame is set, it is reset as minimum frame or maximum frame.
217         /// </remarks>
218         /// <example>
219         /// Let's say myLottie.json file has 100 frames originally, then it will have 0 ~ 99 range of frame index.
220         /// <code>
221         /// LottieAnimationView myLottie = new LottieAnimationView();
222         /// myLottie.URL = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "myLottie.json"; //myLottie.json's total frame is 100 (frame: 0~99)
223         /// Window.Instance.GetDefaultLayer().Add(myLottie);
224         /// myLottie.CurrentFrame = 200; //display 99 frame
225         /// myLottie.SetMinMaxFrame(10, 20);
226         /// myLottie.CurrentFrame = 15; //display 15 frame
227         /// myLottie.CurrentFrame = 50; //display 20 frame, because the MinMax is set (10,20) above
228         /// </code>
229         /// </example>
230         /// <since_tizen> 7 </since_tizen>
231         public int CurrentFrame
232         {
233             set
234             {
235                 currentStates.frame = value;
236                 tlog.Fatal(tag, $"<[{GetId()}]SET frame={currentStates.frame}>");
237                 DoAction(vectorImageVisualIndex, (int)actionType.jumpTo, new PropertyValue(currentStates.frame));
238             }
239             get
240             {
241                 int ret = 0;
242                 PropertyMap map = Image;
243                 if (map != null)
244                 {
245                     PropertyValue val = map.Find(ImageVisualProperty.CurrentFrameNumber);
246                     if (val != null)
247                     {
248                         if (val.Get(out ret))
249                         {
250                             //tlog.Fatal(tag,  $"CurrentFrameNumber get! val={ret}");
251                             return ret;
252                         }
253                     }
254                 }
255                 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get CurrentFrameNumber from dali!! ret={ret}>");
256                 return ret;
257             }
258         }
259
260         /// <summary>
261         /// Set or Get looping mode of Lottie animation.
262         /// </summary>
263         /// <since_tizen> 7 </since_tizen>
264         public LoopingModeType LoopingMode
265         {
266             set
267             {
268                 currentStates.loopMode = (LoopingModeType)value;
269                 currentStates.changed = true;
270
271                 tlog.Fatal(tag, $"<[{GetId()}] SET loopMode={currentStates.loopMode}>");
272                 PropertyMap map = new PropertyMap();
273                 map.Add(ImageVisualProperty.LoopingMode, new PropertyValue((int)currentStates.loopMode));
274                 DoAction(vectorImageVisualIndex, (int)actionType.updateProperty, new PropertyValue(map));
275             }
276             get
277             {
278                 //tlog.Fatal(tag,  $"LoopMode get!");
279                 PropertyMap map = base.Image;
280                 var ret = 0;
281                 if (map != null)
282                 {
283                     PropertyValue val = map.Find(ImageVisualProperty.LoopingMode);
284                     if (val != null)
285                     {
286                         if (val.Get(out ret))
287                         {
288                             //tlog.Fatal(tag,  $"gotten LoopMode={ret}");
289                             if (ret != (int)currentStates.loopMode && ret > 0)
290                             {
291                                 tlog.Fatal(tag, $" [ERROR][{GetId()}](LottieAnimationView) different LoopMode! gotten={ret}, loopMode={currentStates.loopMode}");
292                             }
293                             currentStates.loopMode = (LoopingModeType)ret;
294                             return (LoopingModeType)ret;
295                         }
296                     }
297                 }
298                 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get loopMode from dali>");
299                 return currentStates.loopMode;
300             }
301         }
302
303         /// <summary>
304         /// Set or Get loop count. 
305         /// </summary>
306         /// <remarks>
307         /// Minus value means infinite loop count.
308         /// </remarks>
309         /// <example>
310         /// <code>
311         /// LottieAnimationView myLottie = new LottieAnimationView();
312         /// myLottie.URL = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "myLottie.json"; //myLottie.json's total frame is 100 (frame: 0~99)
313         /// Window.Instance.GetDefaultLayer().Add(myLottie);
314         /// myLottie.LoopCount = -1; //infinite loop
315         /// myLottie.Play();
316         /// myLottie.Stop(); //it plays continuously unless Stop() is called
317         /// myLottie.LoopCount = 2;
318         /// myLottie.Play(); //it plays only 2 times and stops automatically
319         /// </code>
320         /// </example>
321         /// <since_tizen> 7 </since_tizen>
322         public int LoopCount
323         {
324             set
325             {
326                 currentStates.changed = true;
327                 currentStates.loopCount = value;
328                 tlog.Fatal(tag, $"<[{GetId()}]SET currentStates.loopCount={currentStates.loopCount}>");
329                 PropertyMap map = new PropertyMap();
330                 map.Add(ImageVisualProperty.LoopCount, new PropertyValue(currentStates.loopCount));
331                 DoAction(vectorImageVisualIndex, (int)actionType.updateProperty, new PropertyValue(map));
332             }
333             get
334             {
335                 //tlog.Fatal(tag,  $"LoopCount get!");
336                 PropertyMap map = base.Image;
337                 var ret = 0;
338                 if (map != null)
339                 {
340                     PropertyValue val = map.Find(ImageVisualProperty.LoopCount);
341                     if (val != null)
342                     {
343                         if (val.Get(out ret))
344                         {
345                             //tlog.Fatal(tag,  $"gotten loop count={ret}");
346                             if (ret != currentStates.loopCount && ret > 0)
347                             {
348                                 tlog.Fatal(tag, $"<[ERROR][{GetId()}](LottieAnimationView) different loop count! gotten={ret}, loopCount={currentStates.loopCount}>");
349                             }
350                             currentStates.loopCount = ret;
351                             return currentStates.loopCount;
352                         }
353                     }
354                 }
355                 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get LoopCount from dali  currentStates.loopCount={currentStates.loopCount}>");
356                 return currentStates.loopCount;
357             }
358         }
359
360         /// <summary>
361         /// Set or Get stop behavior.
362         /// </summary>
363         /// <since_tizen> 7 </since_tizen>
364         public StopBehaviorType StopBehavior
365         {
366             set
367             {
368                 currentStates.stopEndAction = (StopBehaviorType)value;
369                 currentStates.changed = true;
370
371                 tlog.Fatal(tag, $"<[{GetId()}]SET val={currentStates.stopEndAction}>");
372                 PropertyMap map = new PropertyMap();
373                 map.Add(ImageVisualProperty.StopBehavior, new PropertyValue((int)currentStates.stopEndAction));
374                 DoAction(vectorImageVisualIndex, (int)actionType.updateProperty, new PropertyValue(map));
375             }
376             get
377             {
378                 //tlog.Fatal(tag,  $"StopBehavior get!");
379                 PropertyMap map = base.Image;
380                 var ret = 0;
381                 if (map != null)
382                 {
383                     PropertyValue val = map.Find(ImageVisualProperty.StopBehavior);
384                     if (val != null)
385                     {
386                         if (val.Get(out ret))
387                         {
388                             //tlog.Fatal(tag,  $"gotten StopBehavior={ret}");
389                             if (ret != (int)currentStates.stopEndAction)
390                             {
391                                 tlog.Fatal(tag, $"<[ERROR][{GetId()}](LottieAnimationView) different StopBehavior! gotten={ret}, StopBehavior={currentStates.stopEndAction}>");
392                             }
393                             currentStates.stopEndAction = (StopBehaviorType)ret;
394                             return (StopBehaviorType)ret;
395                         }
396                     }
397                 }
398                 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get StopBehavior from dali>");
399                 return currentStates.stopEndAction;
400             }
401         }
402         #endregion Property
403
404
405         #region Method
406         /// <summary>
407         /// Set minimum and maximum frame.
408         /// </summary>
409         /// <param name="minFrame">minimum frame</param>
410         /// <param name="maxFrame">maximum frame</param>
411         /// <since_tizen> 7 </since_tizen>
412         public void SetMinMaxFrame(int minFrame, int maxFrame)
413         {
414             tlog.Fatal(tag, $"< [{GetId()}] SetPlayRange({minFrame}, {maxFrame})");
415
416             currentStates.changed = true;
417             currentStates.framePlayRangeMin = minFrame;
418             currentStates.framePlayRangeMax = maxFrame;
419
420             PropertyArray array = new PropertyArray();
421             array.PushBack(new PropertyValue(currentStates.framePlayRangeMin));
422             array.PushBack(new PropertyValue(currentStates.framePlayRangeMax));
423
424             PropertyMap map = new PropertyMap();
425             map.Add(ImageVisualProperty.PlayRange, new PropertyValue(array));
426             DoAction(vectorImageVisualIndex, (int)actionType.updateProperty, new PropertyValue(map));
427             tlog.Fatal(tag, $"  [{GetId()}] currentStates.min:({currentStates.framePlayRangeMin}, max:{currentStates.framePlayRangeMax})>");
428         }
429
430         /// <summary>
431         /// Play Animation.
432         /// </summary>
433         /// <since_tizen> 7 </since_tizen>
434         public new void Play()
435         {
436             tlog.Fatal(tag, $"<[{GetId()}] Play()");
437             debugPrint();
438             base.Play();
439             tlog.Fatal(tag, $"[{GetId()}]>");
440         }
441
442         /// <summary>
443         /// Pause Animation.
444         /// </summary>
445         /// <since_tizen> 7 </since_tizen>
446         public new void Pause()
447         {
448             tlog.Fatal(tag, $"<[{GetId()}] Pause()>");
449             debugPrint();
450             base.Pause();
451             tlog.Fatal(tag, $"[{GetId()}]>");
452         }
453
454         /// <summary>
455         /// Stop Animation.
456         /// </summary>
457         /// <since_tizen> 7 </since_tizen>
458         public new void Stop()
459         {
460             tlog.Fatal(tag, $"<[{GetId()}] Stop()");
461             debugPrint();
462             base.Stop();
463             tlog.Fatal(tag, $"[{GetId()}]>");
464         }
465
466         /// <summary>
467         /// Get the list of layers' information (start frame, end frame) in Lottie file.
468         /// </summary>
469         /// <returns>List of Tuple (string of layer name, integer of start frame, integer of end frame)</returns>
470         /// <since_tizen> 7 </since_tizen>
471         public List<Tuple<string, int, int>> GetContentInfo()
472         {
473             tlog.Fatal(tag, $"<");
474             if (currentStates.contentInfo != null)
475             {
476                 return currentStates.contentInfo;
477             }
478
479             PropertyMap imageMap = base.Image;
480             PropertyMap contentMap = new PropertyMap();
481             if (imageMap != null)
482             {
483                 PropertyValue val = imageMap.Find(ImageVisualProperty.ContentInfo);
484                 if (val != null)
485                 {
486                     if (val.Get(contentMap))
487                     {
488                         for (uint i = 0; i < contentMap.Count(); i++)
489                         {
490                             string key = contentMap.GetKeyAt(i).StringKey;
491                             PropertyArray arr = new PropertyArray();
492                             contentMap.GetValue(i).Get(arr);
493                             if (arr != null)
494                             {
495                                 int startFrame, endFrame;
496                                 arr.GetElementAt(0).Get(out startFrame);
497                                 arr.GetElementAt(1).Get(out endFrame);
498
499                                 tlog.Fatal(tag, $"[{i}] layer name={key}, startFrame={startFrame}, endFrame={endFrame}");
500
501                                 Tuple<string, int, int> item = new Tuple<string, int, int>(key, startFrame, endFrame);
502
503                                 currentStates.contentInfo?.Add(item);
504                             }
505                         }
506                     }
507                 }
508             }
509             tlog.Fatal(tag, $">");
510             return currentStates.contentInfo;
511         }
512
513         /// <summary>
514         /// A marker has its start frame and end frame. 
515         /// Animation will play between the start frame and the end frame of the marker if one marker is specified.
516         /// Or animation will play between the start frame of the first marker and the end frame of the second marker if two markers are specified.   *
517         /// </summary>
518         /// <param name="marker1">First marker</param>
519         /// <param name="marker2">Second marker</param>
520         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
521         [EditorBrowsable(EditorBrowsableState.Never)]
522         public void SetMinMaxFrameByMarker(string marker1, string marker2 = null)
523         {
524             tlog.Fatal(tag, $"< [{GetId()}] SetMinMaxFrameByMarker({marker1}, {marker2})");
525
526             currentStates.changed = true;
527             currentStates.mark1 = marker1;
528             currentStates.mark2 = marker2;
529
530             PropertyArray array = new PropertyArray();
531             array.PushBack(new PropertyValue(currentStates.mark1));
532             if (marker2 != null)
533             {
534                 array.PushBack(new PropertyValue(currentStates.mark2));
535             }
536
537             PropertyMap map = new PropertyMap();
538             map.Add(ImageVisualProperty.PlayRange, new PropertyValue(array));
539             DoAction(vectorImageVisualIndex, (int)actionType.updateProperty, new PropertyValue(map));
540             tlog.Fatal(tag, $"  [{GetId()}] currentStates.mark1:{currentStates.mark1}, mark2:{currentStates.mark2} >");
541         }
542         #endregion Method
543
544
545         #region Event, Enum, Struct, ETC
546         /// <summary>
547         /// Animation finished event.
548         /// </summary>
549         /// <since_tizen> 7 </since_tizen>
550         public event EventHandler Finished
551         {
552             add
553             {
554                 if (finishedEventHandler == null)
555                 {
556                     tlog.Fatal(tag, $"<[{GetId()}] Finished eventhandler added>");
557                     visualEventSignalCallback = onVisualEventSignal;
558                     VisualEventSignal().Connect(visualEventSignalCallback);
559                 }
560                 finishedEventHandler += value;
561             }
562             remove
563             {
564                 tlog.Fatal(tag, $"<[{GetId()}] Finished eventhandler removed>");
565                 finishedEventHandler -= value;
566                 if (finishedEventHandler == null && visualEventSignalCallback != null)
567                 {
568                     VisualEventSignal().Disconnect(visualEventSignalCallback);
569                 }
570             }
571         }
572
573         /// <summary>
574         /// Enumeration for what state the vector animation is in
575         /// </summary>
576         /// <since_tizen> 7 </since_tizen>
577         public enum PlayStateType
578         {
579             /// <summary>
580             /// Invalid
581             /// </summary>
582             /// <since_tizen> 7 </since_tizen>
583             Invalid = -1,
584             /// <summary>
585             /// Vector Animation has stopped
586             /// </summary>
587             /// <since_tizen> 7 </since_tizen>
588             Stopped = 0,
589             /// <summary>
590             /// The vector animation is playing
591             /// </summary>
592             /// <since_tizen> 7 </since_tizen>
593             Playing = 1,
594             /// <summary>
595             /// The vector animation is paused
596             /// </summary>
597             /// <since_tizen> 7 </since_tizen>
598             Paused = 2
599         }
600
601         /// <summary>
602         /// Enumeration for what to do when the animation is stopped.
603         /// </summary>
604         /// <since_tizen> 7 </since_tizen>
605         public enum StopBehaviorType
606         {
607             /// <summary>
608             /// When the animation is stopped, the current frame is shown.
609             /// </summary>
610             /// <since_tizen> 7 </since_tizen>
611             CurrentFrame,
612             /// <summary>
613             /// When the animation is stopped, the min frame (first frame) is shown.
614             /// </summary>
615             /// <since_tizen> 7 </since_tizen>
616             MinimumFrame,
617             /// <summary>
618             /// When the animation is stopped, the max frame (last frame) is shown.
619             /// </summary>
620             /// <since_tizen> 7 </since_tizen>
621             MaximumFrame
622         }
623
624         /// <summary>
625         /// Enumeration for what looping mode is in.
626         /// </summary>
627         /// <since_tizen> 7 </since_tizen>
628         public enum LoopingModeType
629         {
630             /// <summary>
631             /// When the animation arrives at the end in looping mode, the animation restarts from the beginning.
632             /// </summary>
633             /// <since_tizen> 7 </since_tizen>
634             Restart,
635             /// <summary>
636             /// When the animation arrives at the end in looping mode, the animation reverses direction and runs backwards again.
637             /// </summary>
638             /// <since_tizen> 7 </since_tizen>
639             AutoReverse
640         }
641         #endregion Event, Enum, Struct, ETC
642
643
644         #region Internal
645         internal class VisualEventSignalArgs : EventArgs
646         {
647             public int VisualIndex
648             {
649                 set;
650                 get;
651             }
652             public int SignalId
653             {
654                 set;
655                 get;
656             }
657         }
658
659         internal event EventHandler<VisualEventSignalArgs> VisualEvent
660         {
661             add
662             {
663                 if (visualEventSignalHandler == null)
664                 {
665                     visualEventSignalCallback = onVisualEventSignal;
666                     VisualEventSignal().Connect(visualEventSignalCallback);
667                 }
668                 visualEventSignalHandler += value;
669             }
670             remove
671             {
672                 visualEventSignalHandler -= value;
673                 if (visualEventSignalHandler == null && VisualEventSignal().Empty() == false)
674                 {
675                     VisualEventSignal().Disconnect(visualEventSignalCallback);
676                 }
677             }
678         }
679
680         internal void EmitVisualEventSignal(int visualIndex, int signalId)
681         {
682             VisualEventSignal().Emit(this, visualIndex, signalId);
683         }
684
685         internal VisualEventSignal VisualEventSignal()
686         {
687             VisualEventSignal ret = new VisualEventSignal(Interop.VisualEventSignal.NewWithView(View.getCPtr(this)), false);
688             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
689             return ret;
690         }
691         #endregion Internal
692
693
694         #region Private
695         private struct states
696         {
697             internal string url;
698             internal int frame;
699             internal int loopCount;
700             internal LoopingModeType loopMode;
701             internal StopBehaviorType stopEndAction;
702             internal int framePlayRangeMin;
703             internal int framePlayRangeMax;
704             internal bool changed;
705             internal int totalFrame;
706             internal float scale;
707             internal PlayStateType playState;
708             internal List<Tuple<string, int, int>> contentInfo;
709             internal string mark1, mark2;
710         };
711         private states currentStates;
712
713         private enum actionType
714         {
715             play,
716             pause,
717             stop,
718             jumpTo,
719             updateProperty,
720         };
721
722         private struct DevelVisual
723         {
724             internal enum Type
725             {
726                 AnimatedGradient = Visual.Type.AnimatedImage + 1,
727                 AnimatedVectorImage = Visual.Type.AnimatedImage + 2,
728             }
729         }
730
731         private const string tag = "NUITEST";
732         private const int vectorImageVisualIndex = 10000000 + 1000 + 2;
733         private event EventHandler finishedEventHandler;
734
735         private void OnFinished()
736         {
737             tlog.Fatal(tag, $"<[{GetId()}] OnFinished()>");
738             finishedEventHandler?.Invoke(this, null);
739         }
740
741         private void onVisualEventSignal(IntPtr targetView, int visualIndex, int signalId)
742         {
743             OnFinished();
744
745             if (targetView != IntPtr.Zero)
746             {
747                 View v = Registry.GetManagedBaseHandleFromNativePtr(targetView) as View;
748                 if (v != null)
749                 {
750                     tlog.Fatal(tag, $"targetView is not null! name={v.Name}");
751                 }
752                 else
753                 {
754                     tlog.Fatal(tag, $"target is something created from dali");
755                 }
756             }
757             VisualEventSignalArgs e = new VisualEventSignalArgs();
758             e.VisualIndex = visualIndex;
759             e.SignalId = signalId;
760             visualEventSignalHandler?.Invoke(this, e);
761
762             tlog.Fatal(tag, $"<[{GetId()}] onVisualEventSignal()! visualIndex={visualIndex}, signalId={signalId}>");
763         }
764
765         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
766         private delegate void VisualEventSignalCallbackType(IntPtr targetView, int visualIndex, int signalId);
767
768         private VisualEventSignalCallbackType visualEventSignalCallback;
769         private EventHandler<VisualEventSignalArgs> visualEventSignalHandler;
770
771         private void debugPrint()
772         {
773             tlog.Fatal(tag, $"===================================");
774             tlog.Fatal(tag, $"<[{GetId()}] get currentStates : url={currentStates.url}, loopCount={currentStates.loopCount}, framePlayRangeMin/Max({currentStates.framePlayRangeMin},{currentStates.framePlayRangeMax}) ");
775             tlog.Fatal(tag, $"  get from Property : StopBehavior={StopBehavior}, LoopMode={LoopingMode}, LoopCount={LoopCount}, PlayState={PlayState} >");
776             tlog.Fatal(tag, $"===================================");
777         }
778         #endregion Private
779     }
780 }