3 using System.Runtime.InteropServices;
4 using System.Collections.Generic;
9 public class ScrollContainer
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;
22 private float _height;
23 private bool _isFocused;
24 private float _marginX;
25 private float _marginY;
26 private float _offsetY;
27 private float _offsetX;
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;
41 public ScrollContainer()
43 _itemSize = new Vector3(0.0f, 0.0f, 0.0f);
47 _currentScrollPosition = 0.0f;
58 _container = new View();
59 _itemRoot = new Actor();
60 _container.Add(_itemRoot);
62 _itemList = new List<View>();
64 if (_panGestureDetector == null)
66 _panGestureDetector = new PanGestureDetector();
67 _panGestureDetector.Attach(_container);
69 _panGestureDetector.Detected += OnPan;
72 _container.ParentOrigin = NDalic.ParentOriginTopLeft;
73 _container.AnchorPoint = NDalic.AnchorPointTopLeft;
74 _itemRoot.ParentOrigin = NDalic.ParentOriginTopLeft;
75 _itemRoot.AnchorPoint = NDalic.AnchorPointTopLeft;
77 _container.WidthResizePolicy = "FILL_TO_PARENT";
78 _container.HeightResizePolicy = "FILL_TO_PARENT";
79 _itemRoot.WidthResizePolicy = "FILL_TO_PARENT";
80 _itemRoot.HeightResizePolicy = "FILL_TO_PARENT";
82 _stage = Stage.GetCurrent();
83 _stageSize = _stage.GetSize();
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);
101 public Dali.View Container
117 public Actor ItemRoot
125 public Vector3 ItemSize
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);
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);
242 public ImageView ShadowBorder
246 return _shadowBorder;
251 _shadowBorder = value;
255 public ImageView SpotLight
268 public int FocusedItemID
272 if (_focusedItem < 0)
281 public Actor GetCurrentFocusedActor()
283 if (_focusedItem < 0)
288 return _itemList[_focusedItem];
291 public void AddItem(View item)
293 item.AnchorPoint = NDalic.AnchorPointBottomCenter;
294 item.ParentOrigin = NDalic.ParentOriginBottomCenter;
296 item.Size = _itemSize;
297 item.SetKeyboardFocusable(true);
298 item.Position = GetItemPosition(_itemCount, _currentScrollPosition);
300 item.Name = _itemCount.ToString();
302 // item.ClippingMode = "CLIP_CHILDREN";
306 _panGestureDetector.Attach(item);
310 // Perform Show animation on ScrollContainer (used only for Poster Container)
313 Container.Add(ItemRoot);
316 _showAnimation = new Animation (0.35f);
318 _showAnimation.AnimateTo(new Property(_container, Actor.Property.COLOR_ALPHA), new Property.Value(1.0f));
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));
325 _showAnimation.Play();
328 // Perform Hide animation on ScrollContainer (used only for Poster Container)
333 _hideAnimation.Clear();
334 _hideAnimation.Reset();
337 float duration = 0.35f;
338 _hideAnimation = new Animation(duration);
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));
343 _hideAnimation.Finished += OnHideAnimationFinished;
346 _hideAnimation.Play();
349 public View Focus(int itemId)
355 else if (itemId >= _itemList.Count)
357 itemId = _itemList.Count - 1;
360 _itemList[itemId].Add(_shadowBorder);
361 _itemList[itemId].Add(_spotLight);
363 // Perform Spot Light animation
364 if(_focusedItem != itemId && _spotLight != null)
366 _spotLightAnimation.Clear();
367 _spotLightAnimation.Animate( _spotLight, _circularPath, new Vector3(0.0f, 0.0f, 0.0f) );
368 _spotLightAnimation.SetLooping(true);
369 _spotLightAnimation.Play();
372 _focusedItem = itemId;
374 Vector3 itemPosition = GetItemPosition(_focusedItem, _currentScrollPosition);
376 _focusAnimation.Clear();
378 float relativeItemPositionX = itemPosition.x - _itemSize.width * 0.5f + (_stageSize.width * 0.5f) + _offsetX;
379 if (relativeItemPositionX < _marginX + _offsetX + _padding)
381 float amount = _marginX + _offsetX + _padding - relativeItemPositionX;
382 Scroll(amount, itemId + 1); // Perform Scroll animation
384 else if (relativeItemPositionX + _itemSize.width + _padding + _marginX > _stageSize.width)
386 float amount = relativeItemPositionX + _marginX + _padding + _itemSize.width - _stageSize.width;
387 Scroll(-amount, itemId - 1); // Perform Scroll animation
391 for (int i = 0; i < _itemList.Count; ++i)
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));
400 for (int i = 0; i < _itemList.Count; ++i)
402 SetupItemRenderer(_itemList[i], false);
404 // Perform Focus animation
405 if (i == _focusedItem)
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));
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));
419 _focusAnimation.Play();
421 if (_isFocused && _focusedItem >= 0)
423 SetupItemRenderer(_itemList[_focusedItem], true);
424 SetupSpotLightRenderer();
427 return _itemList[_focusedItem];
430 // Perform EddenEffect animation on Focused Item specified
431 public void FocusAnimation(EdenEffect edenEffect, EdenEffectDirection direction)
433 edenEffect.FocusAnimation(_itemList[_focusedItem], _itemSize, 1.0f, direction);
436 public void SetFocused(bool focused)
438 _isFocused = focused;
440 // Perform Focus animation if the ScrollContainer is not focused already
443 Actor parent = _shadowBorder.GetParent();
446 parent.Remove(_shadowBorder);
449 parent = _spotLight.GetParent();
452 parent.Remove(_spotLight);
455 _focusTransitionAnimation.Clear();
457 for (int i = 0; i < _itemList.Count; ++i)
459 SetupItemRenderer(_itemList[i], false);
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));
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));
471 _focusTransitionAnimation.Play();
479 // Obtain ID of first visible item/image on the screen of the ScrollContainer
480 public int GetFirstVisibleItemId()
482 int firstItemId = -1;
486 firstItemId = (int)Math.Floor((-1.0 * _currentScrollPosition + _marginX * 2.0f) / (_itemSize.x + _padding));
490 firstItemId = (int)Math.Floor(-1.0 * _currentScrollPosition / (_itemSize.x + _padding));
501 // Obtain ID of last visible item/image on the screen of the ScrollContainer
502 public int GetLastVisibleItemId()
508 lastItemId = (int)Math.Ceiling(((_width - _currentScrollPosition - _marginX * 2.0f) / _itemSize.x) - 1);
512 lastItemId = (int)Math.Ceiling(((_width - _currentScrollPosition) / _itemSize.x) - 1);
515 if (lastItemId >= _itemList.Count)
518 lastItemId = _itemList.Count - 1;
524 // Obtain Next item/image (Right of the currently focused item) of the ScrollContainer
525 public Actor FocusNext()
529 if (_focusedItem < GetFirstVisibleItemId() || _focusedItem > GetLastVisibleItemId())
531 nextItem = GetFirstVisibleItemId();
535 nextItem = _focusedItem + 1;
538 return Focus(nextItem);
541 // Obtain Previous item/image (left of the currently focused item) of the ScrollContainer
542 public Actor FocusPrevious()
544 int previousItem = -1;
546 if (_focusedItem < GetFirstVisibleItemId() || _focusedItem > GetLastVisibleItemId())
548 previousItem = GetFirstVisibleItemId();
552 previousItem = _focusedItem - 1;
555 return Focus(previousItem);
558 private void OnHideAnimationFinished(object source, Animation.FinishedEventArgs e)
560 var currentParent = ItemRoot.GetParent();
561 if (_shouldHide && currentParent != null)
563 Container.Remove(ItemRoot);
567 private void OnPan(object source, PanGestureDetector.DetectedEventArgs e)
569 switch (e.PanGesture.state)
571 case Gesture.State.Started:
572 _scrollDisplacement = 0.0f;
575 case Gesture.State.Continuing:
576 _scrollDisplacement = e.PanGesture.displacement.x;
579 case Gesture.State.Finished:
580 case Gesture.State.Cancelled:
581 float absScrollDistance = _scrollDisplacement;
582 if (absScrollDistance < 0.0f)
583 absScrollDistance = 0.0f - absScrollDistance;
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;
590 if (absScrollDistance > 1.0f && scrollSpeed > 0.05f) // Threshold TBD
592 if (_scrollDisplacement > 0.0f) // scroll constant distance in constant speed.
594 Scroll((_itemSize.x + _padding) * 2, GetFirstVisibleItemId());
598 Scroll(-(_itemSize.x + _padding) * 2, GetFirstVisibleItemId());
605 // Perform ScrollAnimation on each item
606 private void Scroll(float amount, int baseItem)
608 float tagetScrollPosition = _currentScrollPosition + amount;
609 float totalItemSize = _itemList.Count * (_itemSize.width + _padding) + _padding + (_marginX * 2.0f);
611 float maxScrollPosition = _width - totalItemSize;
613 if (tagetScrollPosition < maxScrollPosition)
615 tagetScrollPosition = maxScrollPosition;
617 if (tagetScrollPosition > 0.0f)
619 tagetScrollPosition = 0.0f;
622 _scrollAnimation.Clear();
624 for (int i = 0; i < _itemList.Count; ++i)
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));
632 _currentScrollPosition = tagetScrollPosition;
633 _scrollAnimation.Play();
636 // Calculate Position of any item/image of ScrollContainer
637 private Vector3 GetItemPosition(int itemId, float scrollPosition)
641 // used (_itemSize.width * 0.5f) because of AnchorPointCenter
642 // used (_stageSize.width * 0.5f) because of ParentOriginCenter
643 if (_focusedItem > itemId)
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);
648 else if (_focusedItem == itemId)
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);
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);
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);
666 // Used for SpotLight animation with clipping
667 private void SetupItemRenderer(Actor actor, bool stencilOn)
671 Renderer renderer = actor.GetRendererAt(0);
675 // Setup the renderer properties:
676 // The stencil plane is only for stencilling, so disable writing to color buffer.
678 // Enable stencil. Draw to the stencil buffer (only).
681 renderer.RenderMode = (int)RenderModeType.COLOR_STENCIL;
685 renderer.RenderMode = (int)RenderModeType.COLOR;
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;
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;
700 // This object must be rendered 1st.
701 renderer.DepthIndex = 10;
706 // Used for SpotLight animation with clipping
707 private void SetupSpotLightRenderer()
711 Renderer renderer = _spotLight.GetRendererAt(0);
715 // Setup the renderer properties:
716 // Write to color buffer so soptlight is visible
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;
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;
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;
737 // This object must be rendered last.
738 renderer.DepthIndex = 20;