3 using System.Runtime.InteropServices;
4 using System.Collections.Generic;
9 public class ScrollContainer : CustomView
11 private View _container; // View Container will be the first item added to ScrollContainer and parent to all the items added to the ScrollContainer.
12 private Vector3 _itemSize; // Size of the item / images added to the ScrollContainer.
13 private List<View> _itemList; // List collection of View items/images added to the ScrollContainer.
14 private int _itemCount; // Number of items / images added to the ScrollContainer.
15 private int _focusedItem; // Index of currently focused View item / image on the ScrollContainer.
16 private float _scrollDisplacement; // Used for horizontal pan displacement.
17 private float _currentScrollPosition; // Used for horizontal scroll position.
18 private float _gap; // Used for gap / padding between items / images on the ScrollContainer.
19 private float _width; // Width of the ScrollContainer.
20 private float _height; // Height of the ScrollContainer.
21 private bool _isFocused; // Flag to check if ScrollContainer is enabled or not.
22 private float _marginX; // Extra horizontal margin is used to add an extra gap between items / images after a focused and scaled item / image.
23 private float _marginY; // Extra vertical margin (not used at the moment).
24 private float _offsetYFactor; // Vertical Position offset Factor of item height.
25 private float _offsetX; // Horizontal Position offset of ScrollContainer.
26 private Stage _stage; // Reference to Dali stage.
27 private Vector2 _stageSize; // Reference to Dali stage size.
28 private ImageView _shadowBorder; // Reference to Shadow border ImageView applied to the focused item on ScrollContainer.
29 private ImageView _spotLight; // Reference to SpotLight ImageView applied to the focused item on ScrollContainer.
30 private Animation _spotLightAnimation; // SpotLight Animation applied to the focused item on ScrollContainer.
31 private Animation _focusAnimation; // Focused position animation on ScrollContainer.
32 private Animation _scrollAnimation; // Scroll animation on items of ScrollContainer.
33 private Animation _focusTransitionAnimation; // Focus Transition (scaling /unscaling) animation on items of ScrollContainer.
34 private Path _circularPath; // Circular path used for SpotLight Animation applied to the focused item on ScrollContainer.
36 public ScrollContainer() : base(ViewWrapperImpl.CustomViewBehaviour.DISABLE_STYLE_CHANGE_SIGNALS |
37 ViewWrapperImpl.CustomViewBehaviour.REQUIRES_KEYBOARD_NAVIGATION_SUPPORT)
50 public Dali.View Container
66 public Vector3 ItemSize
77 Vector3 topLeft = new Vector3(-0.25f * _itemSize.width, -0.25f * _itemSize.height, 0.0f);
78 Vector3 topRight = new Vector3(0.25f * _itemSize.width, -0.25f * _itemSize.height, 0.0f);
79 Vector3 bottomRight = new Vector3(0.25f * _itemSize.width, 0.25f * _itemSize.height, 0.0f);
80 Vector3 bottomLeft = new Vector3(-0.25f * _itemSize.width, 0.25f * _itemSize.height, 0.0f);
82 _circularPath = new Path();
83 _circularPath.AddPoint(topLeft);
84 _circularPath.AddPoint(topRight);
85 _circularPath.AddPoint(bottomRight);
86 _circularPath.AddPoint(bottomLeft);
87 _circularPath.AddPoint(topLeft);
88 _circularPath.GenerateControlPoints(0.5f);
118 public float OffsetYFator
122 return _offsetYFactor;
127 _offsetYFactor = value;
183 public ImageView ShadowBorder
187 return _shadowBorder;
192 _shadowBorder = value;
196 public ImageView SpotLight
209 public int FocusedItemID
213 if (_focusedItem < 0)
222 // This override method is called automatically after the Control has been initialized.
223 // Any second phase initialization is done here.
224 public override void OnInitialize()
226 _itemSize = new Vector3(0.0f, 0.0f, 0.0f);
230 _currentScrollPosition = 0.0f;
236 _offsetYFactor = 0.0f;
239 _container = new View();
240 this.Add(_container);
242 _itemList = new List<View>();
244 this.ParentOrigin = NDalic.ParentOriginTopLeft;
245 this.AnchorPoint = NDalic.AnchorPointTopLeft;
246 this.WidthResizePolicy = "FILL_TO_PARENT";
247 this.HeightResizePolicy = "FILL_TO_PARENT";
248 this.SetKeyboardFocusable(true);
250 _container.ParentOrigin = NDalic.ParentOriginTopLeft;
251 _container.AnchorPoint = NDalic.AnchorPointTopLeft;
252 _container.WidthResizePolicy = "FILL_TO_PARENT";
253 _container.HeightResizePolicy = "FILL_TO_PARENT";
255 _stage = Stage.GetCurrent();
256 _stageSize = _stage.GetSize();
258 _spotLightAnimation = new Animation(Constants.SpotLightDuration);
259 _focusTransitionAnimation = new Animation(Constants.FocusTransitionDuration);
260 _focusAnimation = new Animation(Constants.FocusDuration);
261 _focusAnimation.SetEndAction(Animation.EndAction.BakeFinal);
262 _scrollAnimation = new Animation(Constants.ScrollDuration);
263 _scrollAnimation.SetEndAction(Animation.EndAction.BakeFinal);
265 EnableGestureDetection(Gesture.Type.Pan);
268 // This will be invoked automatically if an item/image is added to the ScrollContainer
269 public override void OnChildAdd(Actor actor)
271 View item = View.DownCast(actor);
273 if (item is View && item != _container)
275 item.AnchorPoint = NDalic.AnchorPointBottomCenter;
276 item.ParentOrigin = NDalic.ParentOriginBottomCenter;
278 item.Size = _itemSize;
279 item.SetKeyboardFocusable(true);
280 item.Position = GetItemPosition(_itemCount, _currentScrollPosition);
282 item.Name = _itemCount.ToString();
284 //item.ClippingMode = "CLIP_CHILDREN";
286 _container.Add(item);
290 item.SetKeyboardFocusable(true);
294 // This will be invoked automatically if an item/image is removed from the ScrollContainer
295 public override void OnChildRemove(Actor actor)
297 View item = View.DownCast(actor);
299 if (item is View && item != _container)
301 _container.Remove(item);
304 _itemList.Remove(item);
308 // This override function supports two dimensional keyboard navigation.
309 // This function returns the next keyboard focusable actor in ScrollContainer control towards the given direction.
310 public override Actor GetNextKeyboardFocusableActor(Actor currentFocusedActor, View.KeyboardFocus.Direction direction, bool loopEnabled)
312 if (direction == View.KeyboardFocus.Direction.LEFT)
314 return FocusPrevious(loopEnabled);
316 else if (direction == View.KeyboardFocus.Direction.RIGHT)
318 return FocusNext(loopEnabled);
322 return currentFocusedActor;
326 // This override function is invoked before chosen focusable actor will be focused.
327 // This allows the application to preform any actions (i.e. Scroll and SpotLight animations) before the focus is actually moved to the chosen actor.
328 public override void OnKeyboardFocusChangeCommitted(Actor commitedFocusableActor)
333 // This override function is invoked whenever a pan gesture is detected on this control.
334 // Perform Scroll Animation based upon pan gesture velocity / speed.
335 public override void OnPan(PanGesture pan)
339 case Gesture.State.Started:
340 _scrollDisplacement = 0.0f;
343 case Gesture.State.Continuing:
344 _scrollDisplacement = pan.displacement.x;
347 case Gesture.State.Finished:
348 case Gesture.State.Cancelled:
349 float absScrollDistance = _scrollDisplacement;
350 if (absScrollDistance < 0.0f)
351 absScrollDistance = 0.0f - absScrollDistance;
353 float scrollSpeed = pan.velocity.x * pan.velocity.x + pan.velocity.y * pan.velocity.y;
354 float maxScrollSpeed = 40.0f; // TBD
355 if (scrollSpeed > maxScrollSpeed)
356 scrollSpeed = maxScrollSpeed;
358 if (absScrollDistance > 1.0f && scrollSpeed > 0.05f) // Threshold TBD
360 if (_scrollDisplacement > 0.0f) // scroll constant distance in constant speed.
362 Scroll((_itemSize.x + _gap) * 2, GetFirstVisibleItemId());
366 Scroll(-(_itemSize.x + _gap) * 2, GetFirstVisibleItemId());
373 // This function returns current focused actor
374 public View GetCurrentFocusedActor()
376 if (_focusedItem < 0)
381 return _itemList[_focusedItem];
384 public void SetFocused(bool focused)
386 _isFocused = focused;
388 // Perform Focus animation if the ScrollContainer is not focused already
391 Actor parent = _shadowBorder.GetParent();
394 parent.Remove(_shadowBorder);
397 parent = _spotLight.GetParent();
400 parent.Remove(_spotLight);
403 _focusTransitionAnimation.Clear();
405 for (int i = 0; i < _itemList.Count; ++i)
407 SetupItemRenderer(_itemList[i], false);
409 Vector3 targetPosition = GetItemPosition(i, _currentScrollPosition);
410 _focusTransitionAnimation.AnimateTo(new Dali.Property(_itemList[i], Actor.Property.POSITION),
411 new Dali.Property.Value(targetPosition),
412 new AlphaFunction(AlphaFunction.BuiltinFunction.EASE_OUT_SINE));
414 _focusTransitionAnimation.AnimateTo(new Dali.Property(_itemList[i], Actor.Property.SCALE),
415 new Dali.Property.Value(new Vector3(1.0f, 1.0f, 1.0f)),
416 new AlphaFunction(AlphaFunction.BuiltinFunction.EASE_OUT_SINE));
419 _focusTransitionAnimation.Play();
427 // Obtain ID of first visible item/image on the screen of the ScrollContainer
428 private int GetFirstVisibleItemId()
430 int firstItemId = -1;
434 firstItemId = (int)Math.Floor((-1.0 * _currentScrollPosition + _marginX * 2.0f) / (_itemSize.x + _gap));
438 firstItemId = (int)Math.Floor(-1.0 * _currentScrollPosition / (_itemSize.x + _gap));
449 // Obtain ID of last visible item/image on the screen of the ScrollContainer
450 private int GetLastVisibleItemId()
456 lastItemId = (int)Math.Ceiling(((_width - _currentScrollPosition - _marginX * 2.0f) / _itemSize.x) - 1);
460 lastItemId = (int)Math.Ceiling(((_width - _currentScrollPosition) / _itemSize.x) - 1);
463 if (lastItemId >= _itemList.Count)
466 lastItemId = _itemList.Count - 1;
472 // Obtain Next item/image (Right of the currently focused item) of the ScrollContainer
473 private Actor FocusNext(bool loopEnabled)
477 if (_focusedItem < GetFirstVisibleItemId() || _focusedItem > GetLastVisibleItemId())
479 nextItem = GetFirstVisibleItemId();
483 nextItem = _focusedItem + 1;
486 if (nextItem >= _itemList.Count)
494 nextItem = _itemList.Count - 1;
498 _focusedItem = nextItem;
499 return _itemList[_focusedItem];
502 // Obtain Previous item/image (left of the currently focused item) of the ScrollContainer
503 private Actor FocusPrevious(bool loopEnabled)
505 int previousItem = -1;
507 if (_focusedItem < GetFirstVisibleItemId() || _focusedItem > GetLastVisibleItemId())
509 previousItem = GetFirstVisibleItemId();
513 previousItem = _focusedItem - 1;
516 if (previousItem < 0)
520 previousItem = _itemList.Count - 1;
528 _focusedItem = previousItem;
529 return _itemList[_focusedItem];
532 // Perform ScrollAnimation on each item
533 private void Scroll(float amount, int baseItem)
535 float tagetScrollPosition = _currentScrollPosition + amount;
536 float totalItemSize = _itemList.Count * (_itemSize.width + _gap) + _gap + (_marginX * 2.0f);
538 float maxScrollPosition = _width - totalItemSize;
540 if (tagetScrollPosition < maxScrollPosition)
542 tagetScrollPosition = maxScrollPosition;
544 if (tagetScrollPosition > 0.0f)
546 tagetScrollPosition = 0.0f;
548 _scrollAnimation.Clear();
550 for (int i = 0; i < _itemList.Count; ++i)
552 Vector3 targetPosition = GetItemPosition(i, tagetScrollPosition);
553 _scrollAnimation.AnimateTo(new Dali.Property(_itemList[i], Actor.Property.POSITION),
554 new Dali.Property.Value(targetPosition),
555 new AlphaFunction(AlphaFunction.BuiltinFunction.EASE_OUT_SINE));
558 _currentScrollPosition = tagetScrollPosition;
559 _scrollAnimation.Play();
562 // This function uses ItemId as next FocusedItem and preforms Scroll and SpotLight animations on that item.
563 private void Focus(int itemId)
569 else if (itemId >= _itemList.Count)
571 itemId = _itemList.Count - 1;
574 _itemList[itemId].Add(_shadowBorder);
575 _itemList[itemId].Add(_spotLight);
577 // Perform Spot Light animation
578 if(_focusedItem != itemId && _spotLight != null)
580 _spotLightAnimation.Clear();
581 _spotLightAnimation.Animate( _spotLight, _circularPath, new Vector3(0.0f, 0.0f, 0.0f) );
582 _spotLightAnimation.SetLooping(true);
583 _spotLightAnimation.Play();
586 _focusedItem = itemId;
588 Vector3 itemPosition = GetItemPosition(_focusedItem, _currentScrollPosition);
590 _focusAnimation.Clear();
592 float relativeItemPositionX = itemPosition.x - _itemSize.width * 0.5f + (_stageSize.width * 0.5f) + _offsetX;
593 if (relativeItemPositionX < _marginX + _offsetX + _gap)
595 float amount = _marginX + _offsetX + _gap - relativeItemPositionX;
596 Scroll(amount, itemId + 1); // Perform Scroll animation
598 else if (relativeItemPositionX + _itemSize.width + _gap + _marginX > _stageSize.width)
600 float amount = relativeItemPositionX + _marginX + _gap + _itemSize.width - _stageSize.width;
601 Scroll(-amount, itemId - 1); // Perform Scroll animation
605 // Perform animation when item is focused
606 for (int i = 0; i < _itemList.Count; ++i)
608 Vector3 targetPosition = GetItemPosition(i, _currentScrollPosition);
609 _focusAnimation.AnimateTo(new Dali.Property(_itemList[i], Actor.Property.POSITION),
610 new Dali.Property.Value(targetPosition),
611 new AlphaFunction(AlphaFunction.BuiltinFunction.EASE_OUT_SINE));
615 for (int i = 0; i < _itemList.Count; ++i)
617 SetupItemRenderer(_itemList[i], false);
619 // Perform scale animation on Focused item
620 if (i == _focusedItem)
622 _focusAnimation.AnimateTo(new Dali.Property(_itemList[i], Actor.Property.SCALE),
623 new Dali.Property.Value(new Vector3(1.2f, 1.2f, 1.2f)),
624 new AlphaFunction(AlphaFunction.BuiltinFunction.EASE_OUT_SINE));
628 _focusAnimation.AnimateTo(new Dali.Property(_itemList[i], Actor.Property.SCALE),
629 new Dali.Property.Value(new Vector3(1.0f, 1.0f, 1.0f)),
630 new AlphaFunction(AlphaFunction.BuiltinFunction.EASE_OUT_SINE));
634 _focusAnimation.Play();
636 if (_isFocused && _focusedItem >= 0)
638 SetupItemRenderer(_itemList[_focusedItem], true);
639 SetupSpotLightRenderer();
643 // Calculate Position of any item/image of ScrollContainer
644 private Vector3 GetItemPosition(int itemId, float scrollPosition)
648 // used (_itemSize.width * 0.5f) because of AnchorPointCenter
649 // used (_stageSize.width * 0.5f) because of ParentOriginCenter
650 if (_focusedItem > itemId)
652 float positionX = (_itemSize.width * itemId) + (_gap * (itemId + 1)) + scrollPosition + (_itemSize.width * 0.5f) - (_stageSize.width * 0.5f);
653 return new Vector3(positionX, -_itemSize.height * _offsetYFactor, 0.0f);
655 else if (_focusedItem == itemId)
657 float positionX = (_itemSize.width * itemId) + (_gap * (itemId + 1)) + scrollPosition + _marginX + (_itemSize.width * 0.5f) - (_stageSize.width * 0.5f);
658 return new Vector3(positionX, -_itemSize.height * _offsetYFactor, 0.0f);
662 float positionX = (_itemSize.width * itemId) + (_gap * (itemId + 1)) + scrollPosition + _marginX * 2.0f + (_itemSize.width * 0.5f) - (_stageSize.width * 0.5f);
663 return new Vector3(positionX, -_itemSize.height * _offsetYFactor, 0.0f);
668 float positionX = (_itemSize.width * itemId) + (_gap * (itemId + 1)) + scrollPosition + (_itemSize.width * 0.5f) - (_stageSize.width * 0.5f);
669 return new Vector3(positionX, -_itemSize.height * _offsetYFactor, 0.0f);
673 // Used for SpotLight animation with clipping
674 private void SetupItemRenderer(Actor actor, bool stencilOn)
678 Renderer renderer = actor.GetRendererAt(0);
682 // Setup the renderer properties:
683 // The stencil plane is only for stencilling, so disable writing to color buffer.
685 // Enable stencil. Draw to the stencil buffer (only).
688 renderer.RenderMode = (int)RenderModeType.COLOR_STENCIL;
692 renderer.RenderMode = (int)RenderModeType.COLOR;
694 renderer.StencilFunction = (int)StencilFunctionType.ALWAYS;
695 renderer.StencilFunctionReference = 1;
696 renderer.StencilFunctionMask = 0xFF;
697 renderer.StencilOperationOnFail = (int)StencilOperationType.KEEP;
698 renderer.StencilOperationOnZFail = (int)StencilOperationType.KEEP;
699 renderer.StencilOperationOnZPass = (int)StencilOperationType.REPLACE;
700 renderer.StencilMask = 0xFF;
702 // We don't want to write to the depth buffer.
703 renderer.DepthWriteMode = (int)DepthWriteModeType.OFF;
704 // We don't beed to test the depth buffer.
705 renderer.DepthTestMode = (int)DepthTestModeType.OFF;
707 // This object must be rendered 1st.
708 renderer.DepthIndex = 10;
713 // Used for SpotLight animation with clipping
714 private void SetupSpotLightRenderer()
718 Renderer renderer = _spotLight.GetRendererAt(0);
722 // Setup the renderer properties:
723 // Write to color buffer so soptlight is visible
725 // We use blending to blend the spotlight with the poster image.
726 renderer.BlendMode = (int)BlendModeType.ON;
727 renderer.BlendEquationRgb = (int)BlendEquationType.ADD;
728 renderer.BlendEquationAlpha = (int)BlendEquationType.ADD;
729 renderer.BlendFactorDestRgb = (int)BlendFactorType.ONE;
731 // Enable stencil. Here we only draw to areas within the stencil.
732 renderer.RenderMode = (int)RenderModeType.COLOR_STENCIL;
733 renderer.StencilFunction = (int)StencilFunctionType.EQUAL;
734 renderer.StencilFunctionReference = 1;
735 renderer.StencilFunctionMask = 0xFF;
736 // Don't write to the stencil.
737 renderer.StencilMask = 0x00;
739 // We don't need to write to the depth buffer.
740 renderer.DepthWriteMode = (int)DepthWriteModeType.OFF;
741 // We don't need to test the depth buffer.
742 renderer.DepthTestMode = (int)DepthTestModeType.OFF;
744 // This object must be rendered last.
745 renderer.DepthIndex = 20;