Move Event Handlers to View class
[platform/core/uifw/dali-toolkit.git] / plugins / dali-swig / examples / firstscreen / ScrollContainer.cs
1 using Dali;
2 using System;
3 using System.Runtime.InteropServices;
4 using System.Collections.Generic;
5
6
7 namespace FirstScreen
8 {
9     public class ScrollContainer
10     {
11         private View _container;
12         private Actor _itemRoot;
13         private Vector3 _itemSize;
14         private List<View> _itemList;
15         private int _itemCount;
16         private int _focusedItem;
17         private PanGestureDetector _panGestureDetector;
18         private float _scrollDisplacement;
19         private float _currentScrollPosition;
20         private float _padding;
21         private float _width;
22         private float _height;
23         private bool _isFocused;
24         private float _marginX;
25         private float _marginY;
26         private float _offsetY;
27         private float _offsetX;
28         private Stage _stage;
29         private Vector2 _stageSize;
30         private ImageView _shadowBorder;
31         private ImageView _spotLight;
32         private Animation _spotLightAnimation;
33         private Animation _showAnimation;
34         private Animation _hideAnimation;
35         private Animation _focusAnimation;
36         private Animation _scrollAnimation;
37         private Animation _focusTransitionAnimation;
38         private Path _circularPath;
39         private bool _shouldHide;
40
41         public ScrollContainer()
42         {
43             _itemSize = new Vector3(0.0f, 0.0f, 0.0f);
44             _padding = 0.0f;
45             _width = 0.0f;
46             _height = 0.0f;
47             _currentScrollPosition = 0.0f;
48             _itemCount = 0;
49             _focusedItem = -1;
50             _isFocused = false;
51             _marginX = 50.0f;
52             _marginY = 0.0f;
53             _offsetY = 0.0f;
54             _offsetX = 0.0f;
55
56             _shouldHide = true;
57
58             _container = new View();
59             _itemRoot = new Actor();
60             _container.Add(_itemRoot);
61
62             _itemList = new List<View>();
63
64             if (_panGestureDetector == null)
65             {
66                 _panGestureDetector = new PanGestureDetector();
67                 _panGestureDetector.Attach(_container);
68
69                 _panGestureDetector.Detected += OnPan;
70             }
71
72             _container.ParentOrigin = NDalic.ParentOriginTopLeft;
73             _container.AnchorPoint = NDalic.AnchorPointTopLeft;
74             _itemRoot.ParentOrigin = NDalic.ParentOriginTopLeft;
75             _itemRoot.AnchorPoint = NDalic.AnchorPointTopLeft;
76
77             _container.WidthResizePolicy = "FILL_TO_PARENT";
78             _container.HeightResizePolicy = "FILL_TO_PARENT";
79             _itemRoot.WidthResizePolicy = "FILL_TO_PARENT";
80             _itemRoot.HeightResizePolicy = "FILL_TO_PARENT";
81
82             _stage = Stage.GetCurrent();
83             _stageSize = _stage.GetSize();
84
85             _spotLightAnimation = new Animation(5.0f);
86             _focusTransitionAnimation = new Animation (0.35f);
87             _focusAnimation = new Animation (0.35f);
88             _focusAnimation.SetEndAction(Animation.EndAction.BakeFinal);
89             _scrollAnimation = new Animation (0.35f);
90             _scrollAnimation.SetEndAction(Animation.EndAction.BakeFinal);
91         }
92
93         public bool IsFocused
94         {
95             get
96             {
97                 return _isFocused;
98             }
99         }
100
101         public Dali.View Container
102         {
103             get
104             {
105                 return _container;
106             }
107         }
108
109         public int ItemCount
110         {
111             get
112             {
113                 return _itemCount;
114             }
115         }
116
117         public Actor ItemRoot
118         {
119             get
120             {
121                 return _itemRoot;
122             }
123         }
124
125         public Vector3 ItemSize
126         {
127             get
128             {
129                 return _itemSize;
130             }
131
132             set
133             {
134                 _itemSize = value;
135
136                 Vector3 topLeft = new Vector3(-0.25f * _itemSize.width, -0.25f * _itemSize.height, 0.0f);
137                 Vector3 topRight = new Vector3(0.25f * _itemSize.width, -0.25f * _itemSize.height, 0.0f);
138                 Vector3 bottomRight = new Vector3(0.25f * _itemSize.width, 0.25f * _itemSize.height, 0.0f);
139                 Vector3 bottomLeft = new Vector3(-0.25f * _itemSize.width, 0.25f * _itemSize.height, 0.0f);
140
141                 _circularPath = new Path();
142                 _circularPath.AddPoint(topLeft);
143                 _circularPath.AddPoint(topRight);
144                 _circularPath.AddPoint(bottomRight);
145                 _circularPath.AddPoint(bottomLeft);
146                 _circularPath.AddPoint(topLeft);
147                 _circularPath.GenerateControlPoints(0.5f);
148             }
149         }
150
151         public float Padding
152         {
153             get
154             {
155                 return _padding;
156             }
157
158             set
159             {
160                 _padding = value;
161             }
162         }
163
164         public float MarginX
165         {
166             get
167             {
168                 return _marginX;
169             }
170
171             set
172             {
173                 _marginX = value;
174             }
175         }
176
177         public float OffsetY
178         {
179             get
180             {
181                 return _offsetY;
182             }
183
184             set
185             {
186                 _offsetY = value;
187             }
188         }
189
190         public float OffsetX
191         {
192             get
193             {
194                 return _offsetX;
195             }
196
197             set
198             {
199                 _offsetX = value;
200             }
201         }
202
203         public float MarginY
204         {
205             get
206             {
207                 return _marginY;
208             }
209
210             set
211             {
212                 _marginY = value;
213             }
214         }
215
216         public float Width
217         {
218             get
219             {
220                 return _width;
221             }
222
223             set
224             {
225                 _width = value;
226             }
227         }
228
229         public float Height
230         {
231             get
232             {
233                 return _height;
234             }
235
236             set
237             {
238                 _height = value;
239             }
240         }
241
242         public ImageView ShadowBorder
243         {
244             get
245             {
246                 return _shadowBorder;
247             }
248
249             set
250             {
251                 _shadowBorder = value;
252             }
253         }
254
255         public ImageView SpotLight
256         {
257             get
258             {
259                 return _spotLight;
260             }
261
262             set
263             {
264                 _spotLight = value;
265             }
266         }
267
268         public int FocusedItemID
269         {
270             get
271             {
272                 if (_focusedItem < 0)
273                 {
274                     _focusedItem = 0;
275                 }
276
277                 return _focusedItem;
278             }
279         }
280
281         public Actor GetCurrentFocusedActor()
282         {
283             if (_focusedItem < 0)
284             {
285                 _focusedItem = 0;
286             }
287
288             return _itemList[_focusedItem];
289         }
290
291         public void AddItem(View item)
292         {
293             item.AnchorPoint = NDalic.AnchorPointBottomCenter;
294             item.ParentOrigin = NDalic.ParentOriginBottomCenter;
295
296             item.Size = _itemSize;
297             item.SetKeyboardFocusable(true);
298             item.Position = GetItemPosition(_itemCount, _currentScrollPosition);
299
300             item.Name = _itemCount.ToString();
301
302 //          item.ClippingMode = "CLIP_CHILDREN";
303
304             _itemRoot.Add(item);
305             _itemList.Add(item);
306             _panGestureDetector.Attach(item);
307             _itemCount++;
308         }
309
310         // Perform Show animation on ScrollContainer (used only for Poster Container)
311         public void Show()
312         {
313             Container.Add(ItemRoot);
314
315             _shouldHide = false;
316             _showAnimation = new Animation (0.35f);
317
318             _showAnimation.AnimateTo(new Property(_container, Actor.Property.COLOR_ALPHA), new Property.Value(1.0f));
319
320             _container.PositionY = _container.Position.y + 200.0f;
321             float targetPositionY = _container.Position.y - 200.0f;
322             _showAnimation.AnimateTo(new Property(_container, Actor.Property.POSITION_Y), new Property.Value(targetPositionY),
323                                      new AlphaFunction(AlphaFunction.BuiltinFunction.LINEAR));
324
325             _showAnimation.Play();
326         }
327
328         // Perform Hide animation on ScrollContainer (used only for Poster Container)
329         public void Hide()
330         {
331             if (_hideAnimation)
332             {
333                 _hideAnimation.Clear();
334                 _hideAnimation.Reset();
335             }
336
337             float duration = 0.35f;
338             _hideAnimation = new Animation(duration);
339
340             _hideAnimation.AnimateTo(new Property(_container, Actor.Property.COLOR_ALPHA), new Property.Value(0.0f),
341                                      new AlphaFunction(AlphaFunction.BuiltinFunction.LINEAR), new TimePeriod(0.0f, duration * 0.75f));
342
343             _hideAnimation.Finished += OnHideAnimationFinished;
344
345             _shouldHide = true;
346             _hideAnimation.Play();
347         }
348
349         public View Focus(int itemId)
350         {
351             if (itemId < 0)
352             {
353                 itemId = 0;
354             }
355             else if (itemId >= _itemList.Count)
356             {
357                 itemId = _itemList.Count - 1;
358             }
359
360             _itemList[itemId].Add(_shadowBorder);
361             _itemList[itemId].Add(_spotLight);
362
363             // Perform Spot Light animation
364             if(_focusedItem != itemId && _spotLight != null)
365             {
366                 _spotLightAnimation.Clear();
367                 _spotLightAnimation.Animate( _spotLight, _circularPath, new Vector3(0.0f, 0.0f, 0.0f) );
368                 _spotLightAnimation.SetLooping(true);
369                 _spotLightAnimation.Play();
370             }
371
372             _focusedItem = itemId;
373
374             Vector3 itemPosition = GetItemPosition(_focusedItem, _currentScrollPosition);
375
376             _focusAnimation.Clear();
377
378             float relativeItemPositionX = itemPosition.x - _itemSize.width * 0.5f + (_stageSize.width * 0.5f) + _offsetX;
379             if (relativeItemPositionX < _marginX + _offsetX + _padding)
380             {
381                 float amount = _marginX + _offsetX + _padding - relativeItemPositionX;
382                 Scroll(amount, itemId + 1); // Perform Scroll animation
383             }
384             else if (relativeItemPositionX + _itemSize.width + _padding + _marginX > _stageSize.width)
385             {
386                 float amount = relativeItemPositionX + _marginX + _padding + _itemSize.width - _stageSize.width;
387                 Scroll(-amount, itemId - 1); // Perform Scroll animation
388             }
389             else
390             {
391                 for (int i = 0; i < _itemList.Count; ++i)
392                 {
393                     Vector3 targetPosition = GetItemPosition(i, _currentScrollPosition);
394                     _focusAnimation.AnimateTo(new Property(_itemList[i], Actor.Property.POSITION),
395                                               new Property.Value(targetPosition),
396                                               new AlphaFunction(AlphaFunction.BuiltinFunction.EASE_OUT_SINE));
397                 }
398             }
399
400             for (int i = 0; i < _itemList.Count; ++i)
401             {
402                 SetupItemRenderer(_itemList[i], false);
403
404                 // Perform Focus animation
405                 if (i == _focusedItem)
406                 {
407                     _focusAnimation.AnimateTo(new Property(_itemList[i], Actor.Property.SCALE),
408                                               new Property.Value(new Vector3(1.2f, 1.2f, 1.2f)),
409                                               new AlphaFunction(AlphaFunction.BuiltinFunction.EASE_OUT_SINE));
410                 }
411                 else
412                 {
413                     _focusAnimation.AnimateTo(new Property(_itemList[i], Actor.Property.SCALE),
414                                               new Property.Value(new Vector3(1.0f, 1.0f, 1.0f)),
415                                               new AlphaFunction(AlphaFunction.BuiltinFunction.EASE_OUT_SINE));
416                 }
417             }
418
419             _focusAnimation.Play();
420
421             if (_isFocused && _focusedItem >= 0)
422             {
423                 SetupItemRenderer(_itemList[_focusedItem], true);
424                 SetupSpotLightRenderer();
425             }
426
427             return _itemList[_focusedItem];
428         }
429
430         // Perform EddenEffect animation on Focused Item specified
431         public void FocusAnimation(EdenEffect edenEffect, EdenEffectDirection direction)
432         {
433             edenEffect.FocusAnimation(_itemList[_focusedItem], _itemSize, 1.0f, direction);
434         }
435
436         public void SetFocused(bool focused)
437         {
438             _isFocused = focused;
439
440             // Perform Focus animation if the ScrollContainer is not focused already
441             if (!_isFocused)
442             {
443                 Actor parent = _shadowBorder.GetParent();
444                 if (parent)
445                 {
446                     parent.Remove(_shadowBorder);
447                 }
448
449                 parent = _spotLight.GetParent();
450                 if (parent)
451                 {
452                     parent.Remove(_spotLight);
453                 }
454
455                 _focusTransitionAnimation.Clear();
456
457                 for (int i = 0; i < _itemList.Count; ++i)
458                 {
459                     SetupItemRenderer(_itemList[i], false);
460
461                     Vector3 targetPosition = GetItemPosition(i, _currentScrollPosition);
462                     _focusTransitionAnimation.AnimateTo(new Property(_itemList[i], Actor.Property.POSITION),
463                                                         new Property.Value(targetPosition),
464                                                         new AlphaFunction(AlphaFunction.BuiltinFunction.EASE_OUT_SINE));
465
466                     _focusTransitionAnimation.AnimateTo(new Property(_itemList[i], Actor.Property.SCALE),
467                                                         new Property.Value(new Vector3(1.0f, 1.0f, 1.0f)),
468                                                         new AlphaFunction(AlphaFunction.BuiltinFunction.EASE_OUT_SINE));
469                 }
470
471                 _focusTransitionAnimation.Play();
472             }
473             else
474             {
475                 Focus(_focusedItem);
476             }
477         }
478
479         // Obtain ID of first visible item/image on the screen of the ScrollContainer
480         public int GetFirstVisibleItemId()
481         {
482             int firstItemId = -1;
483
484             if (_isFocused)
485             {
486                 firstItemId = (int)Math.Floor((-1.0 * _currentScrollPosition + _marginX * 2.0f) / (_itemSize.x + _padding));
487             }
488             else
489             {
490                 firstItemId = (int)Math.Floor(-1.0 * _currentScrollPosition / (_itemSize.x + _padding));
491             }
492
493             if (firstItemId < 0)
494             {
495                 firstItemId = 0;
496             }
497
498             return firstItemId;
499         }
500
501         // Obtain ID of last visible item/image on the screen of the ScrollContainer
502         public int GetLastVisibleItemId()
503         {
504             int lastItemId = -1;
505
506             if (_isFocused)
507             {
508                 lastItemId = (int)Math.Ceiling(((_width - _currentScrollPosition - _marginX * 2.0f) / _itemSize.x) - 1);
509             }
510             else
511             {
512                 lastItemId = (int)Math.Ceiling(((_width - _currentScrollPosition) / _itemSize.x) - 1);
513             }
514
515             if (lastItemId >= _itemList.Count)
516             {
517
518                 lastItemId = _itemList.Count - 1;
519             }
520
521             return lastItemId;
522         }
523
524         // Obtain Next item/image (Right of the currently focused item) of the ScrollContainer
525         public Actor FocusNext()
526         {
527             int nextItem = -1;
528
529             if (_focusedItem < GetFirstVisibleItemId() || _focusedItem > GetLastVisibleItemId())
530             {
531                 nextItem = GetFirstVisibleItemId();
532             }
533             else
534             {
535                 nextItem = _focusedItem + 1;
536             }
537
538             return Focus(nextItem);
539         }
540
541         // Obtain Previous item/image (left of the currently focused item) of the ScrollContainer
542         public Actor FocusPrevious()
543         {
544             int previousItem = -1;
545
546             if (_focusedItem < GetFirstVisibleItemId() || _focusedItem > GetLastVisibleItemId())
547             {
548                 previousItem = GetFirstVisibleItemId();
549             }
550             else
551             {
552                 previousItem = _focusedItem - 1;
553             }
554
555             return Focus(previousItem);
556         }
557
558         private void OnHideAnimationFinished(object source, Animation.FinishedEventArgs e)
559         {
560             var currentParent =  ItemRoot.GetParent();
561             if (_shouldHide && currentParent != null)
562             {
563                 Container.Remove(ItemRoot);
564             }
565         }
566
567         private void OnPan(object source, PanGestureDetector.DetectedEventArgs e)
568         {
569             switch (e.PanGesture.state)
570             {
571             case Gesture.State.Started:
572                 _scrollDisplacement = 0.0f;
573                 break;
574
575             case Gesture.State.Continuing:
576                 _scrollDisplacement = e.PanGesture.displacement.x;
577                 break;
578
579             case Gesture.State.Finished:
580             case Gesture.State.Cancelled:
581                 float absScrollDistance = _scrollDisplacement;
582                 if (absScrollDistance < 0.0f)
583                     absScrollDistance = 0.0f - absScrollDistance;
584
585                 float scrollSpeed = e.PanGesture.velocity.x * e.PanGesture.velocity.x + e.PanGesture.velocity.y * e.PanGesture.velocity.y;
586                 float maxScrollSpeed = 40.0f;  // TBD
587                 if (scrollSpeed > maxScrollSpeed)
588                     scrollSpeed = maxScrollSpeed;
589
590                 if (absScrollDistance > 1.0f && scrollSpeed > 0.05f) // Threshold TBD
591                 {
592                     if (_scrollDisplacement > 0.0f) // scroll constant distance in constant speed.
593                     {
594                         Scroll((_itemSize.x + _padding) * 2, GetFirstVisibleItemId());
595                     }
596                     else
597                     {
598                         Scroll(-(_itemSize.x + _padding) * 2, GetFirstVisibleItemId());
599                     }
600                 }
601                 break;
602             }
603         }
604
605         // Perform ScrollAnimation on each item
606         private void Scroll(float amount, int baseItem)
607         {
608             float tagetScrollPosition = _currentScrollPosition + amount;
609             float totalItemSize = _itemList.Count * (_itemSize.width + _padding) + _padding + (_marginX * 2.0f);
610
611             float maxScrollPosition = _width - totalItemSize;
612
613             if (tagetScrollPosition < maxScrollPosition)
614             {
615                 tagetScrollPosition = maxScrollPosition;
616             }
617             if (tagetScrollPosition > 0.0f)
618             {
619                 tagetScrollPosition = 0.0f;
620             }
621
622             _scrollAnimation.Clear();
623
624             for (int i = 0; i < _itemList.Count; ++i)
625             {
626                 Vector3 targetPosition = GetItemPosition(i, tagetScrollPosition);
627                 _scrollAnimation.AnimateTo(new Property(_itemList[i], Actor.Property.POSITION),
628                                            new Property.Value(targetPosition),
629                                            new AlphaFunction(AlphaFunction.BuiltinFunction.EASE_OUT_SINE));
630             }
631
632             _currentScrollPosition = tagetScrollPosition;
633             _scrollAnimation.Play();
634         }
635
636         // Calculate Position of any item/image of ScrollContainer
637         private Vector3 GetItemPosition(int itemId, float scrollPosition)
638         {
639             if (_isFocused)
640             {
641                 // used (_itemSize.width * 0.5f) because of AnchorPointCenter
642                 // used (_stageSize.width * 0.5f) because of ParentOriginCenter
643                 if (_focusedItem > itemId)
644                 {
645                     float positionX = (_itemSize.width * itemId) + (_padding * (itemId + 1)) + scrollPosition + (_itemSize.width * 0.5f) - (_stageSize.width * 0.5f);
646                     return new Vector3(positionX, -_itemSize.height * _offsetY, 0.0f);
647                 }
648                 else if (_focusedItem == itemId)
649                 {
650                     float positionX = (_itemSize.width * itemId) + (_padding * (itemId + 1)) + scrollPosition + _marginX + (_itemSize.width * 0.5f) - (_stageSize.width * 0.5f);
651                     return new Vector3(positionX, -_itemSize.height * _offsetY, 0.0f);
652                 }
653                 else
654                 {
655                     float positionX = (_itemSize.width * itemId) + (_padding * (itemId + 1)) + scrollPosition + _marginX * 2.0f + (_itemSize.width * 0.5f) - (_stageSize.width * 0.5f);
656                     return new Vector3(positionX, -_itemSize.height * _offsetY, 0.0f);
657                 }
658             }
659             else
660             {
661                 float positionX = (_itemSize.width * itemId) + (_padding * (itemId + 1)) + scrollPosition + (_itemSize.width * 0.5f) - (_stageSize.width * 0.5f);
662                 return new Vector3(positionX, -_itemSize.height * _offsetY, 0.0f);
663             }
664         }
665
666         // Used for SpotLight animation with clipping
667         private void SetupItemRenderer(Actor actor, bool stencilOn)
668         {
669             if (actor)
670             {
671                 Renderer renderer = actor.GetRendererAt(0);
672
673                 if (renderer)
674                 {
675                     // Setup the renderer properties:
676                     // The stencil plane is only for stencilling, so disable writing to color buffer.
677
678                     // Enable stencil. Draw to the stencil buffer (only).
679                     if (stencilOn)
680                     {
681                         renderer.RenderMode = (int)RenderModeType.COLOR_STENCIL;
682                     }
683                     else
684                     {
685                         renderer.RenderMode = (int)RenderModeType.COLOR;
686                     }
687                     renderer.StencilFunction = (int)StencilFunctionType.ALWAYS;
688                     renderer.StencilFunctionReference = 1;
689                     renderer.StencilFunctionMask = 0xFF;
690                     renderer.StencilOperationOnFail = (int)StencilOperationType.KEEP;
691                     renderer.StencilOperationOnZFail = (int)StencilOperationType.KEEP;
692                     renderer.StencilOperationOnZPass = (int)StencilOperationType.REPLACE;
693                     renderer.StencilMask = 0xFF;
694
695                     // We don't want to write to the depth buffer.
696                     renderer.DepthWriteMode = (int)DepthWriteModeType.OFF;
697                     // We don't beed to test the depth buffer.
698                     renderer.DepthTestMode = (int)DepthTestModeType.OFF;
699
700                     // This object must be rendered 1st.
701                     renderer.DepthIndex = 10;
702                 }
703             }
704         }
705
706         // Used for SpotLight animation with clipping
707         private void SetupSpotLightRenderer()
708         {
709             if (_spotLight)
710             {
711                 Renderer renderer = _spotLight.GetRendererAt(0);
712
713                 if (renderer)
714                 {
715                     // Setup the renderer properties:
716                     // Write to color buffer so soptlight is visible
717
718                     // We use blending to blend the spotlight with the poster image.
719                     renderer.BlendMode = (int)BlendModeType.ON;
720                     renderer.BlendEquationRgb = (int)BlendEquationType.ADD;
721                     renderer.BlendEquationAlpha = (int)BlendEquationType.ADD;
722                     renderer.BlendFactorDestRgb = (int)BlendFactorType.ONE;
723
724                     // Enable stencil. Here we only draw to areas within the stencil.
725                     renderer.RenderMode = (int)RenderModeType.COLOR_STENCIL;
726                     renderer.StencilFunction = (int)StencilFunctionType.EQUAL;
727                     renderer.StencilFunctionReference = 1;
728                     renderer.StencilFunctionMask = 0xFF;
729                     // Don't write to the stencil.
730                     renderer.StencilMask = 0x00;
731
732                     // We don't need to write to the depth buffer.
733                     renderer.DepthWriteMode = (int)DepthWriteModeType.OFF;
734                     // We don't need to test the depth buffer.
735                     renderer.DepthTestMode = (int)DepthTestModeType.OFF;
736
737                     // This object must be rendered last.
738                     renderer.DepthIndex = 20;
739                 }
740             }
741         }
742     }
743 }
744